Skip to content

Commit 44e0d69

Browse files
michelle0927claudevetrivigneshwaran
authored
feat(ramp): AI-optimize props — drop configure-time dropdowns, add optimized marker (#21802)
* feat(ramp): AI-optimize props — drop configure-time dropdowns, add optimized marker Applies maintainer review feedback to the Ramp MCP action set. Validated non-breaking against the ramp_sandbox eval suite (17/17 pass^2 on Sonnet 5), with the sandbox extending this exact ramp code. - ramp.app.mjs: removed async options() from userId, spendProgramId, departmentId, locationId, transactionId (and the getPropOptions helper) — AI-optimized actions discover IDs via the standalone List actions, not configure-time dropdowns the agent never uses; the props are now plain strings carrying an ID-format example plus the discovery action to run. [patch] - app + 17 optimized actions: added the `// x-pd-ai: optimized` marker (issue-virtual-card excluded — it uses dynamic props). [patch] - list-limits / list-transactions / list-users / list-spend-programs: corrected the compact-default field list in each description to match the actual projection (e.g. display_name not name; added created_at, is_shareable). [patch] - all 15 optimized actions: removed the trailing period after the [See the documentation](...) link so each description ends with the link. [patch] - issue-virtual-card: version bump only — its userId/spendProgramId props lose the dropdown via the app propDefinition change. [patch] App package.json bumped 0.3.0 -> 0.3.1. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * chore(ramp): bump transaction/transfer sources 0.0.3 -> 0.0.4 Dependent version bump on the three Ramp sources alongside the app package.json bump (0.3.0 -> 0.3.1) in this PR; no behavior change. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs(ramp): expand create-user-invite + concretize list-locations example Review feedback on the optimized action set: - create-user-invite: expanded the terse description with purpose, required params (email/name/role/department/location; manager optional), the List Departments/Locations/Users cross-references, the Update User follow-up, and the deferred-invite gotcha (user may not appear in List Users immediately). - list-locations: made the example object fully concrete (id + entity_id UUIDs, not just name) so the location ID format is explicit for downstream actions. Description-only; versions already bumped in this PR (0.0.5 / 0.0.2). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(ramp): flatten issue-virtual-card to static props for MCP Review: this action was the lone holdout still using reloadProps + additionalProps, which are invisible to agents in MCP contexts — a scope gap vs the PR's AI-optimization goal, since every sibling was already flattened. - Replaced the reloadProps/additionalProps branching with fixed props: spendProgramId, allowedCategories, blockedCategories un-hidden; primaryCardEnabled, reimbursementsEnabled, isShareable, limit, interval, transactionAmountLimit declared as static optional props. - Moved the form's conditional required-field enforcement into ConfigurationError guards (linkToSpendProgram <-> spendProgramId must agree; a custom limit needs limit + interval). - run() logic unchanged — same request body, same two modes; behavior-preserving. - Joined the optimized set: added the marker + expanded the description with both modes, cross-references (List Users / List Spend Programs), and a worked example. - Kept linkToSpendProgram (no prop removal -> backwards-compatible). Minor bump 0.0.5 -> 0.1.0. Not eval-covered (card-creation write on the shared Ramp demo account); validated by syntax + behavior-preservation review. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(ramp): align invite description w/ schema, app minor bump, harden limit guard Addresses three review comments: - create-user-invite: description claimed department and location are required, but both inherit optional:true from the app propDefinitions (only email, name, and role are required). Aligned the description to the schema rather than forcing the props required, since the API requirement isn't verifiable here and the schema is the source of truth. - package.json: bumped 0.3.1 -> 0.4.0 — issue-virtual-card is now a minor change (0.1.0), so the app package bumps by the same (minor) segment. - issue-virtual-card: the custom-limit guard only rejected undefined; an empty or whitespace-only limit slipped through to formatUSD (-> undefined amount). Now trims and rejects blank limits too, keeping the both-required rule. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs(ramp): add UUID-format example to create-user-invite description Review: the description named the List Departments/Locations/Users discovery actions but didn't show the ID format. Added a concrete Ramp UUID example for department/location/direct-manager IDs, retaining the discovery guidance. Description-only; no version bump (already 0.0.5 in this PR). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Co-authored-by: vetrivigneshwaran <vicky@pipedream.com>
1 parent 6578964 commit 44e0d69

23 files changed

Lines changed: 112 additions & 189 deletions

File tree

components/ramp/actions/create-department/create-department.mjs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
1+
// x-pd-ai: optimized
12
import ramp from "../../ramp.app.mjs";
23

34
export default {
45
key: "ramp-create-department",
56
name: "Create Department",
6-
description: "Create a new Ramp department (e.g. `Engineering`, `Marketing`). Use this to add an organizational unit before assigning users to it. Returns the new department's `id`, which you can pass to **Update User** to set a user's department, or use to filter **List Transactions** / **List Users**. [See the documentation](https://docs.ramp.com/developer-api/v1/api/departments#post-developer-v1-departments).",
7-
version: "0.0.1",
7+
description: "Create a new Ramp department (e.g. `Engineering`, `Marketing`). Use this to add an organizational unit before assigning users to it. Returns the new department's `id`, which you can pass to **Update User** to set a user's department, or use to filter **List Transactions** / **List Users**. [See the documentation](https://docs.ramp.com/developer-api/v1/api/departments#post-developer-v1-departments)",
8+
version: "0.0.2",
89
type: "action",
910
annotations: {
1011
destructiveHint: false,

components/ramp/actions/create-location/create-location.mjs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
1+
// x-pd-ai: optimized
12
import ramp from "../../ramp.app.mjs";
23

34
export default {
45
key: "ramp-create-location",
56
name: "Create Location",
6-
description: "Create a new Ramp location (e.g. `New York HQ`). Use this to add a site before assigning users to it; the returned `id` can be passed to **Update User** or used to filter **List Transactions** / **List Users**. Associating an entity is optional — set the Entity ID only for multi-entity businesses that scope locations to a specific legal entity. [See the documentation](https://docs.ramp.com/developer-api/v1/api/locations#post-developer-v1-locations).",
7-
version: "0.0.1",
7+
description: "Create a new Ramp location (e.g. `New York HQ`). Use this to add a site before assigning users to it; the returned `id` can be passed to **Update User** or used to filter **List Transactions** / **List Users**. Associating an entity is optional — set the Entity ID only for multi-entity businesses that scope locations to a specific legal entity. [See the documentation](https://docs.ramp.com/developer-api/v1/api/locations#post-developer-v1-locations)",
8+
version: "0.0.2",
89
type: "action",
910
annotations: {
1011
destructiveHint: false,

components/ramp/actions/create-user-invite/create-user-invite.mjs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
1+
// x-pd-ai: optimized
12
import ramp from "../../ramp.app.mjs";
23
import { v4 as uuidv4 } from "uuid";
34

45
export default {
56
key: "ramp-create-user-invite",
67
name: "Create User Invite",
7-
description: "Sends out an invite for a new user. [See the documentation](https://docs.ramp.com/developer-api/v1/reference/rest/users#post-developer-v1-users-deferred)",
8-
version: "0.0.4",
8+
description: "Invite a new person to your Ramp organization by email — use this to onboard an employee who doesn't yet have a Ramp account. Requires their email, first and last name, and `role` (e.g. `BUSINESS_USER`); department, location, and direct manager are optional. Run **List Departments** and **List Locations** to find valid department/location IDs, and **List Users** to find a direct manager's user ID — each is a Ramp UUID, e.g. `fffe6c22-698f-4dc5-b2b1-b35f86947d90`. This is a deferred operation, so the invited user may not appear in **List Users** immediately; once they do, use **Update User** to change their role, department, location, or manager. [See the documentation](https://docs.ramp.com/developer-api/v1/reference/rest/users#post-developer-v1-users-deferred)",
9+
version: "0.0.5",
910
annotations: {
1011
destructiveHint: false,
1112
openWorldHint: true,

components/ramp/actions/get-limit/get-limit.mjs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
1+
// x-pd-ai: optimized
12
import ramp from "../../ramp.app.mjs";
23

34
export default {
45
key: "ramp-get-limit",
56
name: "Get Limit",
6-
description: "Retrieve a single Ramp spend limit by ID. Run the **List Limits** action first to find a valid limit ID. Example: given a limit id from **List Limits**, returns the limit's display name, state (e.g. `ACTIVE`), balance, spend program, and spending restrictions. [See the documentation](https://docs.ramp.com/developer-api/v1/api/funds).",
7-
version: "0.0.1",
7+
description: "Retrieve a single Ramp spend limit by ID. Run the **List Limits** action first to find a valid limit ID. Example: given a limit id from **List Limits**, returns the limit's display name, state (e.g. `ACTIVE`), balance, spend program, and spending restrictions. [See the documentation](https://docs.ramp.com/developer-api/v1/api/funds)",
8+
version: "0.0.2",
89
type: "action",
910
annotations: {
1011
destructiveHint: false,

components/ramp/actions/get-transaction/get-transaction.mjs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
1+
// x-pd-ai: optimized
12
import ramp from "../../ramp.app.mjs";
23

34
export default {
45
key: "ramp-get-transaction",
56
name: "Get Transaction",
6-
description: "Retrieve a single Ramp transaction by ID. Run the **List Transactions** action first to find a valid transaction ID. Example: given a transaction id from **List Transactions**, returns the full transaction — e.g. merchant `Facebook Ads`, amount `$135.00`, category `Advertising`, plus line items and accounting detail. [See the documentation](https://docs.ramp.com/developer-api/v1/api/transactions#get-developer-v1-transactions-transaction-id).",
7-
version: "0.0.1",
7+
description: "Retrieve a single Ramp transaction by ID. Run the **List Transactions** action first to find a valid transaction ID. Example: given a transaction id from **List Transactions**, returns the full transaction — e.g. merchant `Facebook Ads`, amount `$135.00`, category `Advertising`, plus line items and accounting detail. [See the documentation](https://docs.ramp.com/developer-api/v1/api/transactions#get-developer-v1-transactions-transaction-id)",
8+
version: "0.0.2",
89
type: "action",
910
annotations: {
1011
destructiveHint: false,

components/ramp/actions/get-user/get-user.mjs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
1+
// x-pd-ai: optimized
12
import ramp from "../../ramp.app.mjs";
23

34
export default {
45
key: "ramp-get-user",
56
name: "Get User",
6-
description: "Retrieve a single Ramp user by ID. Run the **List Users** action first to find a valid user ID. Example: given a user id from **List Users**, returns that user's full record — name, email, role (e.g. `BUSINESS_OWNER`), department id, and location id. [See the documentation](https://docs.ramp.com/developer-api/v1/api/users#get-developer-v1-users-user-id).",
7-
version: "0.0.1",
7+
description: "Retrieve a single Ramp user by ID. Run the **List Users** action first to find a valid user ID. Example: given a user id from **List Users**, returns that user's full record — name, email, role (e.g. `BUSINESS_OWNER`), department id, and location id. [See the documentation](https://docs.ramp.com/developer-api/v1/api/users#get-developer-v1-users-user-id)",
8+
version: "0.0.2",
89
type: "action",
910
annotations: {
1011
destructiveHint: false,

components/ramp/actions/issue-virtual-card/issue-virtual-card.mjs

Lines changed: 54 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
1+
// x-pd-ai: optimized
12
import ramp from "../../ramp.app.mjs";
3+
import constants from "../../common/constants.mjs";
4+
import { ConfigurationError } from "@pipedream/platform";
25
import { v4 as uuidv4 } from "uuid";
36

47
export default {
58
key: "ramp-issue-virtual-card",
69
name: "Issue Virtual Card",
7-
description: "Creates a new virtual card for a given user. [See the documentation](https://docs.ramp.com/developer-api/v1/reference/rest/limits#post-developer-v1-limits-deferred)",
8-
version: "0.0.5",
10+
description: "Issue a new Ramp virtual card (spend limit) for a user. Run **List Users** to find the user ID. Works two ways: (1) **link to an existing spend program** — set **Link to Existing Spend Program** on and pass a **Spend Program ID** (run **List Spend Programs**), and the program governs the card's restrictions; or (2) **set a custom limit** — leave linking off and pass a **Total Limit per Interval** (e.g. `$500`) and an **Interval** (e.g. `MONTHLY`), optionally a per-transaction cap and allowed/blocked categories. Example: issue a $500/month card by passing the user's ID, `$500`, and `MONTHLY`. [See the documentation](https://docs.ramp.com/developer-api/v1/reference/rest/limits#post-developer-v1-limits-deferred)",
11+
version: "0.1.0",
912
annotations: {
1013
destructiveHint: false,
1114
openWorldHint: true,
@@ -17,7 +20,7 @@ export default {
1720
displayName: {
1821
type: "string",
1922
label: "Virtual Card Name",
20-
description: "The name of the virtual card",
23+
description: "A name for the virtual card, e.g. `Marketing SaaS`.",
2124
},
2225
userId: {
2326
propDefinition: [
@@ -28,86 +31,65 @@ export default {
2831
linkToSpendProgram: {
2932
type: "boolean",
3033
label: "Link to Existing Spend Program",
31-
description: "Whether to link the card to an existing spend program",
32-
reloadProps: true,
34+
description: "Set to true to link this card to an existing spend program (then provide a **Spend Program ID** below). Leave off to set a custom limit instead.",
35+
optional: true,
3336
},
3437
spendProgramId: {
3538
propDefinition: [
3639
ramp,
3740
"spendProgramId",
3841
],
39-
hidden: true,
42+
description: "The spend program to link the card to — a Ramp UUID, e.g. `e9d30f12-c73a-463b-bc5f-b200396359d2`. Run the **List Spend Programs** action to find valid IDs. Required when **Link to Existing Spend Program** is on; leave empty to set a custom limit.",
43+
},
44+
limit: {
45+
type: "string",
46+
label: "Total Limit per Interval (USD)",
47+
description: "Total amount allowed per interval, in USD (e.g. `$500`). Required for a custom limit (when not linking to a spend program); ignored when a Spend Program ID is set.",
48+
optional: true,
49+
},
50+
interval: {
51+
type: "string",
52+
label: "Interval",
53+
description: "Reset interval for the custom limit (e.g. `MONTHLY`). Required for a custom limit.",
54+
options: constants.INTERVALS,
55+
optional: true,
56+
},
57+
transactionAmountLimit: {
58+
type: "string",
59+
label: "Maximum Spend per Transaction (USD)",
60+
description: "Maximum amount per single transaction, in USD (e.g. `$100`). Optional; applies to a custom limit.",
61+
optional: true,
4062
},
4163
allowedCategories: {
4264
propDefinition: [
4365
ramp,
4466
"allowedCategories",
4567
],
46-
hidden: true,
4768
},
4869
blockedCategories: {
4970
propDefinition: [
5071
ramp,
5172
"blockedCategories",
5273
],
53-
hidden: true,
5474
},
55-
},
56-
async additionalProps(props) {
57-
const newProps = {};
58-
if (this.linkToSpendProgram === undefined) {
59-
return newProps;
60-
}
61-
if (this.linkToSpendProgram) {
62-
props.spendProgramId.hidden = false;
63-
} else if (this.linkToSpendProgram === false) {
64-
props.spendProgramId.hidden = true;
65-
props.allowedCategories.hidden = false;
66-
props.blockedCategories.hidden = false;
67-
newProps.primaryCardEnabled = {
68-
type: "boolean",
69-
label: "Primary Card Enabled",
70-
description: "Dictates whether the user's physical card can be linked to this limit",
71-
};
72-
newProps.reimbursementsEnabled = {
73-
type: "boolean",
74-
label: "Reimbursements Enabled",
75-
description: "Dictates whether reimbursements can be submitted against this limit",
76-
};
77-
newProps.isShareable = {
78-
type: "boolean",
79-
label: "Is Shareable",
80-
description: "Dictates whether the spend limit is shareable among multiple users",
81-
optional: true,
82-
};
83-
}
84-
newProps.limit = {
85-
type: "string",
86-
label: "Total Limit per Interval (USD)",
87-
description: "Total amount limit per interval in USD",
88-
};
89-
newProps.interval = {
90-
type: "string",
91-
label: "Interval",
92-
description: "Time interval to apply limit to",
93-
options: [
94-
"ANNUAL",
95-
"DAILY",
96-
"MONTHLY",
97-
"QUARTERLY",
98-
"TERTIARY",
99-
"TOTAL",
100-
"WEEKLY",
101-
"YEARLY",
102-
],
103-
};
104-
newProps.transactionAmountLimit = {
105-
type: "string",
106-
label: "Maximum Spend per Transaction (USD)",
107-
description: "Max amount per transaction in USD",
75+
primaryCardEnabled: {
76+
type: "boolean",
77+
label: "Primary Card Enabled",
78+
description: "Whether the user's physical card can be linked to this limit. Applies to a custom limit.",
10879
optional: true,
109-
};
110-
return newProps;
80+
},
81+
reimbursementsEnabled: {
82+
type: "boolean",
83+
label: "Reimbursements Enabled",
84+
description: "Whether reimbursements can be submitted against this limit. Applies to a custom limit.",
85+
optional: true,
86+
},
87+
isShareable: {
88+
type: "boolean",
89+
label: "Is Shareable",
90+
description: "Whether the spend limit is shareable among multiple users.",
91+
optional: true,
92+
},
11193
},
11294
methods: {
11395
formatUSD(amount) {
@@ -118,6 +100,15 @@ export default {
118100
},
119101
},
120102
async run({ $ }) {
103+
if (this.linkToSpendProgram && !this.spendProgramId) {
104+
throw new ConfigurationError("To link to a spend program, enable it and provide a Spend Program ID — run List Spend Programs to find one.");
105+
}
106+
if (!this.linkToSpendProgram && this.spendProgramId) {
107+
throw new ConfigurationError("A Spend Program ID was provided but 'Link to Existing Spend Program' is off — turn it on to link, or clear the Spend Program ID to set a custom limit.");
108+
}
109+
if (!this.linkToSpendProgram && (this.limit === undefined || this.limit.trim() === "" || this.interval === undefined)) {
110+
throw new ConfigurationError("A custom card limit needs both a Total Limit per Interval (USD) and an Interval — provide both, or link to a spend program instead.");
111+
}
121112
const response = await this.ramp.createLimit({
122113
$,
123114
data: {

components/ramp/actions/list-departments/list-departments.mjs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
1+
// x-pd-ai: optimized
12
import ramp from "../../ramp.app.mjs";
23

34
export default {
45
key: "ramp-list-departments",
56
name: "List Departments",
6-
description: "Retrieve a list of Ramp departments. Use this to find department IDs for other actions such as **Update User** and **List Transactions**. Example: returns `{ id, name }` pairs such as `{ \"id\": \"fffe6c22-698f-4dc5-b2b1-b35f86947d90\", \"name\": \"Engineering\" }` — pass the `id` to **Update User** to set a user's department. Returns one page of up to `pageSize` results (max 100); a `page.next` value in the response means more departments exist beyond this page. [See the documentation](https://docs.ramp.com/developer-api/v1/api/departments#get-developer-v1-departments).",
7-
version: "0.0.1",
7+
description: "Retrieve a list of Ramp departments. Use this to find department IDs for other actions such as **Update User** and **List Transactions**. Example: returns `{ id, name }` pairs such as `{ \"id\": \"fffe6c22-698f-4dc5-b2b1-b35f86947d90\", \"name\": \"Engineering\" }` — pass the `id` to **Update User** to set a user's department. Returns one page of up to `pageSize` results (max 100); a `page.next` value in the response means more departments exist beyond this page. [See the documentation](https://docs.ramp.com/developer-api/v1/api/departments#get-developer-v1-departments)",
8+
version: "0.0.2",
89
type: "action",
910
annotations: {
1011
destructiveHint: false,

components/ramp/actions/list-limits/list-limits.mjs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
1+
// x-pd-ai: optimized
12
import ramp from "../../ramp.app.mjs";
23
import utils from "../../common/utils.mjs";
34

45
export default {
56
key: "ramp-list-limits",
67
name: "List Limits",
7-
description: "Retrieve a paginated list of Ramp spend limits. Returns a compact summary of each limit by default (id, name, state, balance, spend program); use **Get Limit** for the full record, or pass `fields` to include specific extra fields. Use this to find limit IDs for **Get Limit**, **Update Limit**, and **Terminate Limit**. Returns one page of up to `pageSize` results (max 100); a `page.next` value in the response means more limits exist beyond this page. [See the documentation](https://docs.ramp.com/developer-api/v1/api/funds).",
8-
version: "0.0.1",
8+
description: "Retrieve a paginated list of Ramp spend limits. Returns a compact summary of each limit by default (id, display_name, state, balance, spend_program_id, created_at, is_shareable); use **Get Limit** for the full record, or pass `fields` to include specific extra fields. Use this to find limit IDs for **Get Limit**, **Update Limit**, and **Terminate Limit**. Returns one page of up to `pageSize` results (max 100); a `page.next` value in the response means more limits exist beyond this page. [See the documentation](https://docs.ramp.com/developer-api/v1/api/funds)",
9+
version: "0.0.2",
910
type: "action",
1011
annotations: {
1112
destructiveHint: false,

components/ramp/actions/list-locations/list-locations.mjs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
1+
// x-pd-ai: optimized
12
import ramp from "../../ramp.app.mjs";
23

34
export default {
45
key: "ramp-list-locations",
56
name: "List Locations",
6-
description: "Retrieve a list of Ramp locations. Use this to find location IDs for other actions such as **Update User** and **List Transactions**. Example: returns `{ id, name, entity_id }` such as `{ \"name\": \"New York HQ\" }`. Returns one page of up to `pageSize` results (max 100); a `page.next` value in the response means more locations exist beyond this page. [See the documentation](https://docs.ramp.com/developer-api/v1/api/locations#get-developer-v1-locations).",
7-
version: "0.0.1",
7+
description: "Retrieve a list of Ramp locations. Use this to find location IDs for other actions such as **Update User** and **List Transactions**. Example: returns objects like `{ \"id\": \"961c6f01-5719-4f4c-8fef-4096a031f32a\", \"name\": \"New York HQ\", \"entity_id\": \"c0e5f8a2-9b1d-4e3a-8f6c-2d7b4a1e9f30\" }` — pass the `id` as the location ID in downstream actions. Returns one page of up to `pageSize` results (max 100); a `page.next` value in the response means more locations exist beyond this page. [See the documentation](https://docs.ramp.com/developer-api/v1/api/locations#get-developer-v1-locations)",
8+
version: "0.0.2",
89
type: "action",
910
annotations: {
1011
destructiveHint: false,

0 commit comments

Comments
 (0)