saasfunnels 0.2.1 → 0.2.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -0
- package/dist/library.js +342 -7
- package/dist/saasfunnels.js +319 -7
- package/dist/types/capabilities.d.ts +1 -1
- package/dist/types/identity.d.ts +1 -1
- package/dist/types/mcp.d.ts +10 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -73,6 +73,12 @@ Hosted interactive MCP uses Clerk OAuth at the application `/mcp` URL and does n
|
|
|
73
73
|
|
|
74
74
|
The application imports the exact versioned MCP registry through the server-side `saasfunnels/library` export. This keeps hosted Streamable HTTP and local stdio MCP on one package owner; it is not a browser API.
|
|
75
75
|
|
|
76
|
+
In hosted OAuth, `list_workspaces` discovers accessible workspace names and IDs. Live read, inspect, and plan tools accept an optional `workspace_id`. The hosted application must select the sole accessible workspace automatically, require an explicit ID when several are accessible, recheck authorization for every call, and include the selected workspace name and ID in each live response. The application passes the selection through to its developer API as the `workspace_id` query parameter. Local stdio API-key tools do not expose workspace selection; their key stays pinned to its workspace.
|
|
77
|
+
|
|
78
|
+
For Funnel discovery, hosted MCP offers `list_funnels` and `get_funnel_state`. `list_funnels` returns at most 50 summaries per page; follow `page.hasMore` with the next `offset` to cover the workspace. The app returns the selected workspace and evaluation time with each read. `get_funnel_state` reports the current lifecycle and publication readiness for one Funnel. These reads do not include Funnel definitions, customer data, or Results. Signals remain supporting evidence and enrollment inputs.
|
|
79
|
+
|
|
80
|
+
For performance, `get_portfolio_results` and `get_funnel_results` use a 7d, 30d, 90d, or 12m reporting window (30d by default). Portfolio rows are paged at 50 Funnels and cover at most the first 100; check `page.has_more` and `funnels_truncated` before claiming complete coverage. Stage counts and revenue rows retain their distinct attribution, value basis, and currency. `list_opportunities` and `list_leads` return at most 50 records per page with `page.total_count` and `page.has_more`; `get_opportunity` and `get_lead` return one bounded record. Opportunity reads omit private evidence and money; Lead reads omit personal identity and raw responses. These six reads require hosted OAuth, an accessible selected workspace, and the app's `funnels.view` role check.
|
|
81
|
+
|
|
76
82
|
## Development
|
|
77
83
|
|
|
78
84
|
```bash
|
package/dist/library.js
CHANGED
|
@@ -24151,6 +24151,10 @@ var registry2 = Object.freeze({
|
|
|
24151
24151
|
"local_mcp",
|
|
24152
24152
|
"api_key"
|
|
24153
24153
|
]),
|
|
24154
|
+
"funnel.workspace.read": capability("funnel.workspace.read", "read", [
|
|
24155
|
+
"in_app",
|
|
24156
|
+
"hosted_mcp"
|
|
24157
|
+
]),
|
|
24154
24158
|
"setup.test_event.send": capability("setup.test_event.send", "external_write", [
|
|
24155
24159
|
"local_cli",
|
|
24156
24160
|
"local_mcp",
|
|
@@ -24260,7 +24264,7 @@ function validateFeatureCatalogManifest(value) {
|
|
|
24260
24264
|
|
|
24261
24265
|
// src/identity.ts
|
|
24262
24266
|
var SAASFUNNELS_CLI_NAME = "saasfunnels";
|
|
24263
|
-
var SAASFUNNELS_CLI_VERSION = "0.2.
|
|
24267
|
+
var SAASFUNNELS_CLI_VERSION = "0.2.3";
|
|
24264
24268
|
var SAASFUNNELS_PRODUCT_NAME = "SaaSFunnels";
|
|
24265
24269
|
var SAASFUNNELS_DEFAULT_API_BASE_URL = "https://app.saasfunnels.ai";
|
|
24266
24270
|
var SAASFUNNELS_ENV = Object.freeze({
|
|
@@ -24297,10 +24301,19 @@ var saasFunnelsMcpToolCapabilityIds = {
|
|
|
24297
24301
|
apply_funnel_entry: "funnel.entry.apply",
|
|
24298
24302
|
generate_direct_api_handoff: "developer.handoff.generate",
|
|
24299
24303
|
get_integration_health: "developer.workspace.read",
|
|
24304
|
+
get_funnel_results: "funnel.workspace.read",
|
|
24305
|
+
get_funnel_state: "funnel.workspace.read",
|
|
24306
|
+
get_lead: "funnel.workspace.read",
|
|
24307
|
+
get_opportunity: "funnel.workspace.read",
|
|
24308
|
+
get_portfolio_results: "funnel.workspace.read",
|
|
24300
24309
|
get_signal_payload_preview: "developer.workspace.read",
|
|
24301
24310
|
get_workspace_readiness: "developer.workspace.read",
|
|
24311
|
+
list_workspaces: "developer.workspace.read",
|
|
24302
24312
|
inspect_funnel_entry: "funnel.entry.inspect",
|
|
24313
|
+
list_funnels: "funnel.workspace.read",
|
|
24314
|
+
list_leads: "funnel.workspace.read",
|
|
24303
24315
|
list_mapping_gaps: "developer.workspace.read",
|
|
24316
|
+
list_opportunities: "funnel.workspace.read",
|
|
24304
24317
|
list_recent_signals: "developer.workspace.read",
|
|
24305
24318
|
simulate_account_funnel_fit: "account.funnel_fit.simulate",
|
|
24306
24319
|
propose_funnel_entry_installation: "funnel.entry.plan",
|
|
@@ -24379,6 +24392,42 @@ function stringArg(args, name) {
|
|
|
24379
24392
|
const value = args[name];
|
|
24380
24393
|
return typeof value === "string" && value.trim() ? value.trim() : void 0;
|
|
24381
24394
|
}
|
|
24395
|
+
var uuidPattern = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
|
24396
|
+
var resultsWindows = ["7d", "30d", "90d", "12m"];
|
|
24397
|
+
var attributionRoles = ["direct", "influenced", "observed"];
|
|
24398
|
+
var opportunityStates = ["detected", "open", "eligible", "queued", "presented", "engaged", "action_pending", "converted", "dismissed", "suppressed", "expired", "failed"];
|
|
24399
|
+
var leadStates = ["new", "working", "qualified", "converted", "disqualified", "withdrawn", "deleted"];
|
|
24400
|
+
function uuidArg(args, name, required2 = false) {
|
|
24401
|
+
const value = args[name];
|
|
24402
|
+
if (value === void 0 && !required2) return void 0;
|
|
24403
|
+
const parsed = stringArg(args, name);
|
|
24404
|
+
if (!parsed || !uuidPattern.test(parsed)) {
|
|
24405
|
+
throw new McpProtocolError(-32602, `${name} must be a UUID.`);
|
|
24406
|
+
}
|
|
24407
|
+
return parsed;
|
|
24408
|
+
}
|
|
24409
|
+
function integerArg(args, name, fallback, max) {
|
|
24410
|
+
const value = args[name] ?? fallback;
|
|
24411
|
+
if (!Number.isInteger(value) || Number(value) < (name === "limit" ? 1 : 0) || Number(value) > max) {
|
|
24412
|
+
throw new McpProtocolError(-32602, `${name} must be an integer from ${name === "limit" ? 1 : 0} to ${max}.`);
|
|
24413
|
+
}
|
|
24414
|
+
return Number(value);
|
|
24415
|
+
}
|
|
24416
|
+
function optionalFilterArg(args, name) {
|
|
24417
|
+
if (args[name] === void 0) return void 0;
|
|
24418
|
+
const value = stringArg(args, name);
|
|
24419
|
+
if (!value || value.length > 80) {
|
|
24420
|
+
throw new McpProtocolError(-32602, `${name} must be a non-empty string of at most 80 characters.`);
|
|
24421
|
+
}
|
|
24422
|
+
return value;
|
|
24423
|
+
}
|
|
24424
|
+
function resultsWindowArg(args) {
|
|
24425
|
+
const value = args.window ?? "30d";
|
|
24426
|
+
if (typeof value !== "string" || !resultsWindows.includes(value)) {
|
|
24427
|
+
throw new McpProtocolError(-32602, "window must be 7d, 30d, 90d, or 12m.");
|
|
24428
|
+
}
|
|
24429
|
+
return value;
|
|
24430
|
+
}
|
|
24382
24431
|
function sourceArg(args) {
|
|
24383
24432
|
const source = stringArg(args, "source") ?? "direct";
|
|
24384
24433
|
if (!sourceNames.has(source)) {
|
|
@@ -24399,7 +24448,7 @@ function objectArg(args, name) {
|
|
|
24399
24448
|
function sanitizeString(value) {
|
|
24400
24449
|
const withoutSecrets = value.replace(
|
|
24401
24450
|
tokenPattern,
|
|
24402
|
-
(candidate) => /^[A-Z0-9_]+$/.test(candidate) ? candidate : "[redacted-token]"
|
|
24451
|
+
(candidate) => /^[A-Z0-9_]+$/.test(candidate) || /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(candidate) ? candidate : "[redacted-token]"
|
|
24403
24452
|
).replace(emailPattern3, "[redacted-email]");
|
|
24404
24453
|
try {
|
|
24405
24454
|
const parsed = new URL(
|
|
@@ -24668,6 +24717,33 @@ function emptyLiveInputSchema() {
|
|
|
24668
24717
|
type: "object"
|
|
24669
24718
|
};
|
|
24670
24719
|
}
|
|
24720
|
+
var hostedWorkspaceToolNames = /* @__PURE__ */ new Set([
|
|
24721
|
+
"get_integration_health",
|
|
24722
|
+
"get_funnel_results",
|
|
24723
|
+
"get_funnel_state",
|
|
24724
|
+
"get_lead",
|
|
24725
|
+
"get_opportunity",
|
|
24726
|
+
"get_portfolio_results",
|
|
24727
|
+
"get_workspace_readiness",
|
|
24728
|
+
"list_mapping_gaps",
|
|
24729
|
+
"list_recent_signals",
|
|
24730
|
+
"get_signal_payload_preview",
|
|
24731
|
+
"inspect_funnel_entry",
|
|
24732
|
+
"list_funnels",
|
|
24733
|
+
"list_leads",
|
|
24734
|
+
"list_opportunities",
|
|
24735
|
+
"propose_funnel_entry_installation",
|
|
24736
|
+
"get_account_strategy",
|
|
24737
|
+
"simulate_account_funnel_fit"
|
|
24738
|
+
]);
|
|
24739
|
+
function developerPathForWorkspace(path, args, options) {
|
|
24740
|
+
if (!options.hostedMode) return path;
|
|
24741
|
+
if ("workspace_id" in args && !stringArg(args, "workspace_id")) {
|
|
24742
|
+
throw new McpProtocolError(-32602, "workspace_id must be a non-empty string.");
|
|
24743
|
+
}
|
|
24744
|
+
const workspaceId = stringArg(args, "workspace_id");
|
|
24745
|
+
return workspaceId ? `${path}${path.includes("?") ? "&" : "?"}workspace_id=${encodeURIComponent(workspaceId)}` : path;
|
|
24746
|
+
}
|
|
24671
24747
|
function saasFunnelsMcpToolDefinitions({
|
|
24672
24748
|
allowFunnelTests = false,
|
|
24673
24749
|
allowFunnelWrites = false,
|
|
@@ -24859,6 +24935,136 @@ function saasFunnelsMcpToolDefinitions({
|
|
|
24859
24935
|
}
|
|
24860
24936
|
];
|
|
24861
24937
|
if (includeHostedAccountTools) {
|
|
24938
|
+
const readAnnotations = {
|
|
24939
|
+
idempotentHint: true,
|
|
24940
|
+
openWorldHint: true,
|
|
24941
|
+
readOnlyHint: true
|
|
24942
|
+
};
|
|
24943
|
+
const windowSchema = { default: "30d", enum: resultsWindows, type: "string" };
|
|
24944
|
+
const uuidSchema = { format: "uuid", type: "string" };
|
|
24945
|
+
tools.push({
|
|
24946
|
+
annotations: readAnnotations,
|
|
24947
|
+
description: "Read bounded portfolio Results for one workspace and reporting window (30d by default). Funnel rows are paged within the first 100; stage counts, acquisition aggregates, and revenue value bases stay distinct. Check page.has_more and funnels_truncated for coverage.",
|
|
24948
|
+
inputSchema: {
|
|
24949
|
+
additionalProperties: false,
|
|
24950
|
+
properties: {
|
|
24951
|
+
attribution_role: { enum: attributionRoles, type: "string" },
|
|
24952
|
+
funnel_id: uuidSchema,
|
|
24953
|
+
limit: { type: "integer", minimum: 1, maximum: 50 },
|
|
24954
|
+
objective: { enum: ["convert", "grow", "save"], type: "string" },
|
|
24955
|
+
offset: { type: "integer", minimum: 0, maximum: 100 },
|
|
24956
|
+
window: windowSchema
|
|
24957
|
+
},
|
|
24958
|
+
type: "object"
|
|
24959
|
+
},
|
|
24960
|
+
name: "get_portfolio_results",
|
|
24961
|
+
title: "Get Portfolio Results"
|
|
24962
|
+
});
|
|
24963
|
+
tools.push({
|
|
24964
|
+
annotations: readAnnotations,
|
|
24965
|
+
description: "Read one Funnel's Results for a reporting window, including stage counts, configured acquisition aggregates, maturity, warnings, and separate revenue rows. Bounded arrays carry truncation flags.",
|
|
24966
|
+
inputSchema: {
|
|
24967
|
+
additionalProperties: false,
|
|
24968
|
+
properties: { funnel_id: uuidSchema, window: windowSchema },
|
|
24969
|
+
required: ["funnel_id"],
|
|
24970
|
+
type: "object"
|
|
24971
|
+
},
|
|
24972
|
+
name: "get_funnel_results",
|
|
24973
|
+
title: "Get Funnel Results"
|
|
24974
|
+
});
|
|
24975
|
+
tools.push({
|
|
24976
|
+
annotations: readAnnotations,
|
|
24977
|
+
description: "List up to 50 Opportunities in one workspace, optionally filtered by Funnel or lifecycle state. Returns exact total count and page.has_more; no raw evidence or monetary values.",
|
|
24978
|
+
inputSchema: {
|
|
24979
|
+
additionalProperties: false,
|
|
24980
|
+
properties: {
|
|
24981
|
+
funnel_id: uuidSchema,
|
|
24982
|
+
limit: { type: "integer", minimum: 1, maximum: 50 },
|
|
24983
|
+
offset: { type: "integer", minimum: 0, maximum: 25e4 },
|
|
24984
|
+
state: { enum: opportunityStates, type: "string" }
|
|
24985
|
+
},
|
|
24986
|
+
type: "object"
|
|
24987
|
+
},
|
|
24988
|
+
name: "list_opportunities",
|
|
24989
|
+
title: "List Opportunities"
|
|
24990
|
+
});
|
|
24991
|
+
tools.push({
|
|
24992
|
+
annotations: readAnnotations,
|
|
24993
|
+
description: "Read one Opportunity's bounded Funnel, account, lifecycle, delivery, and outcome summary in the selected workspace. No private evidence or monetary values.",
|
|
24994
|
+
inputSchema: {
|
|
24995
|
+
additionalProperties: false,
|
|
24996
|
+
properties: { opportunity_id: uuidSchema },
|
|
24997
|
+
required: ["opportunity_id"],
|
|
24998
|
+
type: "object"
|
|
24999
|
+
},
|
|
25000
|
+
name: "get_opportunity",
|
|
25001
|
+
title: "Get Opportunity"
|
|
25002
|
+
});
|
|
25003
|
+
tools.push({
|
|
25004
|
+
annotations: readAnnotations,
|
|
25005
|
+
description: "List up to 50 generated Leads in one workspace with source Funnel, lifecycle, delivery, and capture counts. Returns exact total count and page.has_more; no personal identity or raw responses.",
|
|
25006
|
+
inputSchema: {
|
|
25007
|
+
additionalProperties: false,
|
|
25008
|
+
properties: {
|
|
25009
|
+
funnel_id: uuidSchema,
|
|
25010
|
+
limit: { type: "integer", minimum: 1, maximum: 50 },
|
|
25011
|
+
offset: { type: "integer", minimum: 0, maximum: 25e4 },
|
|
25012
|
+
state: { enum: leadStates, type: "string" }
|
|
25013
|
+
},
|
|
25014
|
+
type: "object"
|
|
25015
|
+
},
|
|
25016
|
+
name: "list_leads",
|
|
25017
|
+
title: "List Leads"
|
|
25018
|
+
});
|
|
25019
|
+
tools.push({
|
|
25020
|
+
annotations: readAnnotations,
|
|
25021
|
+
description: "Read one generated Lead's bounded source, lifecycle, delivery, capture, and verified outcome summary in the selected workspace. No personal identity or raw responses.",
|
|
25022
|
+
inputSchema: {
|
|
25023
|
+
additionalProperties: false,
|
|
25024
|
+
properties: { lead_id: uuidSchema },
|
|
25025
|
+
required: ["lead_id"],
|
|
25026
|
+
type: "object"
|
|
25027
|
+
},
|
|
25028
|
+
name: "get_lead",
|
|
25029
|
+
title: "Get Lead"
|
|
25030
|
+
});
|
|
25031
|
+
tools.push({
|
|
25032
|
+
annotations: {
|
|
25033
|
+
idempotentHint: true,
|
|
25034
|
+
openWorldHint: true,
|
|
25035
|
+
readOnlyHint: true
|
|
25036
|
+
},
|
|
25037
|
+
description: "List one selected workspace's Funnels with lifecycle, draft, and publication state. Results include evaluation time, total count, and pagination; use offset to read more pages. No Funnel definitions or customer data.",
|
|
25038
|
+
inputSchema: {
|
|
25039
|
+
additionalProperties: false,
|
|
25040
|
+
properties: {
|
|
25041
|
+
include_archived: { type: "boolean" },
|
|
25042
|
+
limit: { type: "integer", minimum: 1, maximum: 50 },
|
|
25043
|
+
offset: { type: "integer", minimum: 0 }
|
|
25044
|
+
},
|
|
25045
|
+
type: "object"
|
|
25046
|
+
},
|
|
25047
|
+
name: "list_funnels",
|
|
25048
|
+
title: "List Funnels"
|
|
25049
|
+
});
|
|
25050
|
+
tools.push({
|
|
25051
|
+
annotations: {
|
|
25052
|
+
idempotentHint: true,
|
|
25053
|
+
openWorldHint: true,
|
|
25054
|
+
readOnlyHint: true
|
|
25055
|
+
},
|
|
25056
|
+
description: "Read one Funnel's current lifecycle, draft and published revisions, and server-derived publication readiness in the selected workspace. No definitions or private evidence.",
|
|
25057
|
+
inputSchema: {
|
|
25058
|
+
additionalProperties: false,
|
|
25059
|
+
properties: {
|
|
25060
|
+
funnel_id: { type: "string", format: "uuid" }
|
|
25061
|
+
},
|
|
25062
|
+
required: ["funnel_id"],
|
|
25063
|
+
type: "object"
|
|
25064
|
+
},
|
|
25065
|
+
name: "get_funnel_state",
|
|
25066
|
+
title: "Get Funnel State"
|
|
25067
|
+
});
|
|
24862
25068
|
tools.push({
|
|
24863
25069
|
annotations: {
|
|
24864
25070
|
idempotentHint: true,
|
|
@@ -25030,6 +25236,29 @@ function hostedSaaSFunnelsMcpToolDefinitions() {
|
|
|
25030
25236
|
capability2 && agentCapabilityAllowedAtBoundary(capability2.id, "hosted_mcp")
|
|
25031
25237
|
);
|
|
25032
25238
|
});
|
|
25239
|
+
for (const tool of tools) {
|
|
25240
|
+
if (!hostedWorkspaceToolNames.has(tool.name)) continue;
|
|
25241
|
+
const properties = tool.inputSchema.properties;
|
|
25242
|
+
properties.workspace_id = {
|
|
25243
|
+
description: "Workspace ID from list_workspaces. Required when your OAuth identity can access multiple workspaces; optional when it can access one. The server verifies access for every call.",
|
|
25244
|
+
type: "string"
|
|
25245
|
+
};
|
|
25246
|
+
}
|
|
25247
|
+
tools.push({
|
|
25248
|
+
annotations: {
|
|
25249
|
+
idempotentHint: true,
|
|
25250
|
+
openWorldHint: true,
|
|
25251
|
+
readOnlyHint: true
|
|
25252
|
+
},
|
|
25253
|
+
description: "List the names and IDs of workspaces accessible to the OAuth user. Use a workspace ID from this list to target live tools when more than one workspace is accessible.",
|
|
25254
|
+
inputSchema: {
|
|
25255
|
+
additionalProperties: false,
|
|
25256
|
+
properties: {},
|
|
25257
|
+
type: "object"
|
|
25258
|
+
},
|
|
25259
|
+
name: "list_workspaces",
|
|
25260
|
+
title: "List Workspaces"
|
|
25261
|
+
});
|
|
25033
25262
|
assertMcpToolCapabilityContract(tools, "hosted_mcp");
|
|
25034
25263
|
return tools;
|
|
25035
25264
|
}
|
|
@@ -25055,7 +25284,7 @@ async function developerGet(path, args, options) {
|
|
|
25055
25284
|
});
|
|
25056
25285
|
}
|
|
25057
25286
|
const response = await (options.fetch ?? fetch)(
|
|
25058
|
-
`${apiBaseUrl(options, args)}${path}`,
|
|
25287
|
+
`${apiBaseUrl(options, args)}${developerPathForWorkspace(path, args, options)}`,
|
|
25059
25288
|
{
|
|
25060
25289
|
headers: {
|
|
25061
25290
|
authorization
|
|
@@ -25091,7 +25320,7 @@ async function developerPost(path, args, body, options, requiredScope) {
|
|
|
25091
25320
|
});
|
|
25092
25321
|
}
|
|
25093
25322
|
const response = await (options.fetch ?? fetch)(
|
|
25094
|
-
`${apiBaseUrl(options, args)}${path}`,
|
|
25323
|
+
`${apiBaseUrl(options, args)}${developerPathForWorkspace(path, args, options)}`,
|
|
25095
25324
|
{
|
|
25096
25325
|
body: JSON.stringify(body),
|
|
25097
25326
|
headers: {
|
|
@@ -25161,11 +25390,17 @@ async function sendTestEvent(args, options) {
|
|
|
25161
25390
|
}
|
|
25162
25391
|
async function callSaaSFunnelsMcpTool(name, args, options = {}) {
|
|
25163
25392
|
try {
|
|
25164
|
-
if (
|
|
25393
|
+
if (!options.hostedMode && "workspace_id" in args && hostedWorkspaceToolNames.has(name)) {
|
|
25394
|
+
throw new McpProtocolError(
|
|
25395
|
+
-32602,
|
|
25396
|
+
"workspace_id is available only with hosted OAuth; an API key is pinned to its workspace."
|
|
25397
|
+
);
|
|
25398
|
+
}
|
|
25399
|
+
if ((name === "get_account_strategy" || name === "get_funnel_results" || name === "get_funnel_state" || name === "get_lead" || name === "get_opportunity" || name === "get_portfolio_results" || name === "list_funnels" || name === "list_leads" || name === "list_opportunities" || name === "simulate_account_funnel_fit" || name === "list_workspaces") && !options.hostedMode) {
|
|
25165
25400
|
throw new McpToolExecutionError(
|
|
25166
|
-
"This
|
|
25401
|
+
"This tool requires hosted OAuth.",
|
|
25167
25402
|
{
|
|
25168
|
-
error: "This
|
|
25403
|
+
error: "This tool requires hosted OAuth.",
|
|
25169
25404
|
ok: false,
|
|
25170
25405
|
required_boundary: "hosted_mcp"
|
|
25171
25406
|
}
|
|
@@ -25208,6 +25443,106 @@ async function callSaaSFunnelsMcpTool(name, args, options = {}) {
|
|
|
25208
25443
|
options
|
|
25209
25444
|
)
|
|
25210
25445
|
);
|
|
25446
|
+
if (name === "list_workspaces")
|
|
25447
|
+
return structuredToolResult(
|
|
25448
|
+
await developerGet("/api/developer-tools/workspaces", {}, options)
|
|
25449
|
+
);
|
|
25450
|
+
if (name === "list_funnels") {
|
|
25451
|
+
const limit = args.limit ?? 25;
|
|
25452
|
+
const offset = args.offset ?? 0;
|
|
25453
|
+
if (!Number.isInteger(limit) || Number(limit) < 1 || Number(limit) > 50 || !Number.isInteger(offset) || Number(offset) < 0 || Number(offset) > 25e4 || args.include_archived !== void 0 && typeof args.include_archived !== "boolean") {
|
|
25454
|
+
throw new McpProtocolError(
|
|
25455
|
+
-32602,
|
|
25456
|
+
"limit must be 1\u201350, offset must be 0\u2013250000, and include_archived must be boolean."
|
|
25457
|
+
);
|
|
25458
|
+
}
|
|
25459
|
+
const query = new URLSearchParams({
|
|
25460
|
+
include_archived: String(args.include_archived === true),
|
|
25461
|
+
limit: String(limit),
|
|
25462
|
+
offset: String(offset)
|
|
25463
|
+
});
|
|
25464
|
+
return structuredToolResult(
|
|
25465
|
+
await developerGet(`/api/developer-tools/funnels?${query}`, args, options)
|
|
25466
|
+
);
|
|
25467
|
+
}
|
|
25468
|
+
if (name === "get_funnel_state") {
|
|
25469
|
+
const funnelId = uuidArg(args, "funnel_id", true);
|
|
25470
|
+
return structuredToolResult(
|
|
25471
|
+
await developerGet(
|
|
25472
|
+
`/api/developer-tools/funnels/${encodeURIComponent(funnelId)}`,
|
|
25473
|
+
args,
|
|
25474
|
+
options
|
|
25475
|
+
)
|
|
25476
|
+
);
|
|
25477
|
+
}
|
|
25478
|
+
if (name === "get_portfolio_results") {
|
|
25479
|
+
const query = new URLSearchParams({
|
|
25480
|
+
window: resultsWindowArg(args),
|
|
25481
|
+
limit: String(integerArg(args, "limit", 50, 50)),
|
|
25482
|
+
offset: String(integerArg(args, "offset", 0, 100))
|
|
25483
|
+
});
|
|
25484
|
+
const funnelId = uuidArg(args, "funnel_id");
|
|
25485
|
+
if (funnelId) query.set("funnel_id", funnelId);
|
|
25486
|
+
const objective = optionalFilterArg(args, "objective");
|
|
25487
|
+
if (objective) {
|
|
25488
|
+
if (!["convert", "grow", "save"].includes(objective)) {
|
|
25489
|
+
throw new McpProtocolError(-32602, "objective must be convert, grow, or save.");
|
|
25490
|
+
}
|
|
25491
|
+
query.set("objective", objective);
|
|
25492
|
+
}
|
|
25493
|
+
const attributionRole = optionalFilterArg(args, "attribution_role");
|
|
25494
|
+
if (attributionRole) {
|
|
25495
|
+
if (!attributionRoles.includes(attributionRole)) {
|
|
25496
|
+
throw new McpProtocolError(-32602, "attribution_role must be direct, influenced, or observed.");
|
|
25497
|
+
}
|
|
25498
|
+
query.set("attribution_role", attributionRole);
|
|
25499
|
+
}
|
|
25500
|
+
return structuredToolResult(
|
|
25501
|
+
await developerGet(`/api/developer-tools/results?${query}`, args, options)
|
|
25502
|
+
);
|
|
25503
|
+
}
|
|
25504
|
+
if (name === "get_funnel_results") {
|
|
25505
|
+
const funnelId = uuidArg(args, "funnel_id", true);
|
|
25506
|
+
const query = new URLSearchParams({ window: resultsWindowArg(args) });
|
|
25507
|
+
return structuredToolResult(
|
|
25508
|
+
await developerGet(
|
|
25509
|
+
`/api/developer-tools/funnels/${encodeURIComponent(funnelId)}/results?${query}`,
|
|
25510
|
+
args,
|
|
25511
|
+
options
|
|
25512
|
+
)
|
|
25513
|
+
);
|
|
25514
|
+
}
|
|
25515
|
+
if (name === "list_opportunities" || name === "list_leads") {
|
|
25516
|
+
const query = new URLSearchParams({
|
|
25517
|
+
limit: String(integerArg(args, "limit", 25, 50)),
|
|
25518
|
+
offset: String(integerArg(args, "offset", 0, 25e4))
|
|
25519
|
+
});
|
|
25520
|
+
const funnelId = uuidArg(args, "funnel_id");
|
|
25521
|
+
if (funnelId) query.set("funnel_id", funnelId);
|
|
25522
|
+
const state = optionalFilterArg(args, "state");
|
|
25523
|
+
if (state) {
|
|
25524
|
+
const allowed = name === "list_leads" ? leadStates : opportunityStates;
|
|
25525
|
+
if (!allowed.includes(state)) {
|
|
25526
|
+
throw new McpProtocolError(-32602, `state is not a supported ${name === "list_leads" ? "Lead" : "Opportunity"} state.`);
|
|
25527
|
+
}
|
|
25528
|
+
query.set("state", state);
|
|
25529
|
+
}
|
|
25530
|
+
const resource = name === "list_leads" ? "leads" : "opportunities";
|
|
25531
|
+
return structuredToolResult(
|
|
25532
|
+
await developerGet(`/api/developer-tools/${resource}?${query}`, args, options)
|
|
25533
|
+
);
|
|
25534
|
+
}
|
|
25535
|
+
if (name === "get_opportunity" || name === "get_lead") {
|
|
25536
|
+
const resource = name === "get_lead" ? "leads" : "opportunities";
|
|
25537
|
+
const id = uuidArg(args, name === "get_lead" ? "lead_id" : "opportunity_id", true);
|
|
25538
|
+
return structuredToolResult(
|
|
25539
|
+
await developerGet(
|
|
25540
|
+
`/api/developer-tools/${resource}/${encodeURIComponent(id)}`,
|
|
25541
|
+
args,
|
|
25542
|
+
options
|
|
25543
|
+
)
|
|
25544
|
+
);
|
|
25545
|
+
}
|
|
25211
25546
|
if (name === "get_workspace_readiness")
|
|
25212
25547
|
return structuredToolResult(
|
|
25213
25548
|
await developerGet(
|
package/dist/saasfunnels.js
CHANGED
|
@@ -24802,7 +24802,7 @@ Applied reviewed edits. Change record: ${result3.changeRecordPath}` : result3.id
|
|
|
24802
24802
|
|
|
24803
24803
|
// src/identity.ts
|
|
24804
24804
|
var SAASFUNNELS_CLI_NAME = "saasfunnels";
|
|
24805
|
-
var SAASFUNNELS_CLI_VERSION = "0.2.
|
|
24805
|
+
var SAASFUNNELS_CLI_VERSION = "0.2.3";
|
|
24806
24806
|
var SAASFUNNELS_PRODUCT_NAME = "SaaSFunnels";
|
|
24807
24807
|
var SAASFUNNELS_DEFAULT_API_BASE_URL = "https://app.saasfunnels.ai";
|
|
24808
24808
|
var SAASFUNNELS_ENV = Object.freeze({
|
|
@@ -25342,6 +25342,10 @@ var registry2 = Object.freeze({
|
|
|
25342
25342
|
"local_mcp",
|
|
25343
25343
|
"api_key"
|
|
25344
25344
|
]),
|
|
25345
|
+
"funnel.workspace.read": capability("funnel.workspace.read", "read", [
|
|
25346
|
+
"in_app",
|
|
25347
|
+
"hosted_mcp"
|
|
25348
|
+
]),
|
|
25345
25349
|
"setup.test_event.send": capability("setup.test_event.send", "external_write", [
|
|
25346
25350
|
"local_cli",
|
|
25347
25351
|
"local_mcp",
|
|
@@ -25380,10 +25384,19 @@ var saasFunnelsMcpToolCapabilityIds = {
|
|
|
25380
25384
|
apply_funnel_entry: "funnel.entry.apply",
|
|
25381
25385
|
generate_direct_api_handoff: "developer.handoff.generate",
|
|
25382
25386
|
get_integration_health: "developer.workspace.read",
|
|
25387
|
+
get_funnel_results: "funnel.workspace.read",
|
|
25388
|
+
get_funnel_state: "funnel.workspace.read",
|
|
25389
|
+
get_lead: "funnel.workspace.read",
|
|
25390
|
+
get_opportunity: "funnel.workspace.read",
|
|
25391
|
+
get_portfolio_results: "funnel.workspace.read",
|
|
25383
25392
|
get_signal_payload_preview: "developer.workspace.read",
|
|
25384
25393
|
get_workspace_readiness: "developer.workspace.read",
|
|
25394
|
+
list_workspaces: "developer.workspace.read",
|
|
25385
25395
|
inspect_funnel_entry: "funnel.entry.inspect",
|
|
25396
|
+
list_funnels: "funnel.workspace.read",
|
|
25397
|
+
list_leads: "funnel.workspace.read",
|
|
25386
25398
|
list_mapping_gaps: "developer.workspace.read",
|
|
25399
|
+
list_opportunities: "funnel.workspace.read",
|
|
25387
25400
|
list_recent_signals: "developer.workspace.read",
|
|
25388
25401
|
simulate_account_funnel_fit: "account.funnel_fit.simulate",
|
|
25389
25402
|
propose_funnel_entry_installation: "funnel.entry.plan",
|
|
@@ -25462,6 +25475,42 @@ function stringArg(args, name) {
|
|
|
25462
25475
|
const value = args[name];
|
|
25463
25476
|
return typeof value === "string" && value.trim() ? value.trim() : void 0;
|
|
25464
25477
|
}
|
|
25478
|
+
var uuidPattern = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
|
25479
|
+
var resultsWindows = ["7d", "30d", "90d", "12m"];
|
|
25480
|
+
var attributionRoles = ["direct", "influenced", "observed"];
|
|
25481
|
+
var opportunityStates = ["detected", "open", "eligible", "queued", "presented", "engaged", "action_pending", "converted", "dismissed", "suppressed", "expired", "failed"];
|
|
25482
|
+
var leadStates = ["new", "working", "qualified", "converted", "disqualified", "withdrawn", "deleted"];
|
|
25483
|
+
function uuidArg(args, name, required2 = false) {
|
|
25484
|
+
const value = args[name];
|
|
25485
|
+
if (value === void 0 && !required2) return void 0;
|
|
25486
|
+
const parsed = stringArg(args, name);
|
|
25487
|
+
if (!parsed || !uuidPattern.test(parsed)) {
|
|
25488
|
+
throw new McpProtocolError(-32602, `${name} must be a UUID.`);
|
|
25489
|
+
}
|
|
25490
|
+
return parsed;
|
|
25491
|
+
}
|
|
25492
|
+
function integerArg(args, name, fallback, max) {
|
|
25493
|
+
const value = args[name] ?? fallback;
|
|
25494
|
+
if (!Number.isInteger(value) || Number(value) < (name === "limit" ? 1 : 0) || Number(value) > max) {
|
|
25495
|
+
throw new McpProtocolError(-32602, `${name} must be an integer from ${name === "limit" ? 1 : 0} to ${max}.`);
|
|
25496
|
+
}
|
|
25497
|
+
return Number(value);
|
|
25498
|
+
}
|
|
25499
|
+
function optionalFilterArg(args, name) {
|
|
25500
|
+
if (args[name] === void 0) return void 0;
|
|
25501
|
+
const value = stringArg(args, name);
|
|
25502
|
+
if (!value || value.length > 80) {
|
|
25503
|
+
throw new McpProtocolError(-32602, `${name} must be a non-empty string of at most 80 characters.`);
|
|
25504
|
+
}
|
|
25505
|
+
return value;
|
|
25506
|
+
}
|
|
25507
|
+
function resultsWindowArg(args) {
|
|
25508
|
+
const value = args.window ?? "30d";
|
|
25509
|
+
if (typeof value !== "string" || !resultsWindows.includes(value)) {
|
|
25510
|
+
throw new McpProtocolError(-32602, "window must be 7d, 30d, 90d, or 12m.");
|
|
25511
|
+
}
|
|
25512
|
+
return value;
|
|
25513
|
+
}
|
|
25465
25514
|
function sourceArg(args) {
|
|
25466
25515
|
const source = stringArg(args, "source") ?? "direct";
|
|
25467
25516
|
if (!sourceNames.has(source)) {
|
|
@@ -25482,7 +25531,7 @@ function objectArg(args, name) {
|
|
|
25482
25531
|
function sanitizeString(value) {
|
|
25483
25532
|
const withoutSecrets = value.replace(
|
|
25484
25533
|
tokenPattern,
|
|
25485
|
-
(candidate) => /^[A-Z0-9_]+$/.test(candidate) ? candidate : "[redacted-token]"
|
|
25534
|
+
(candidate) => /^[A-Z0-9_]+$/.test(candidate) || /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(candidate) ? candidate : "[redacted-token]"
|
|
25486
25535
|
).replace(emailPattern3, "[redacted-email]");
|
|
25487
25536
|
try {
|
|
25488
25537
|
const parsed = new URL(
|
|
@@ -25751,6 +25800,33 @@ function emptyLiveInputSchema() {
|
|
|
25751
25800
|
type: "object"
|
|
25752
25801
|
};
|
|
25753
25802
|
}
|
|
25803
|
+
var hostedWorkspaceToolNames = /* @__PURE__ */ new Set([
|
|
25804
|
+
"get_integration_health",
|
|
25805
|
+
"get_funnel_results",
|
|
25806
|
+
"get_funnel_state",
|
|
25807
|
+
"get_lead",
|
|
25808
|
+
"get_opportunity",
|
|
25809
|
+
"get_portfolio_results",
|
|
25810
|
+
"get_workspace_readiness",
|
|
25811
|
+
"list_mapping_gaps",
|
|
25812
|
+
"list_recent_signals",
|
|
25813
|
+
"get_signal_payload_preview",
|
|
25814
|
+
"inspect_funnel_entry",
|
|
25815
|
+
"list_funnels",
|
|
25816
|
+
"list_leads",
|
|
25817
|
+
"list_opportunities",
|
|
25818
|
+
"propose_funnel_entry_installation",
|
|
25819
|
+
"get_account_strategy",
|
|
25820
|
+
"simulate_account_funnel_fit"
|
|
25821
|
+
]);
|
|
25822
|
+
function developerPathForWorkspace(path, args, options) {
|
|
25823
|
+
if (!options.hostedMode) return path;
|
|
25824
|
+
if ("workspace_id" in args && !stringArg(args, "workspace_id")) {
|
|
25825
|
+
throw new McpProtocolError(-32602, "workspace_id must be a non-empty string.");
|
|
25826
|
+
}
|
|
25827
|
+
const workspaceId = stringArg(args, "workspace_id");
|
|
25828
|
+
return workspaceId ? `${path}${path.includes("?") ? "&" : "?"}workspace_id=${encodeURIComponent(workspaceId)}` : path;
|
|
25829
|
+
}
|
|
25754
25830
|
function saasFunnelsMcpToolDefinitions({
|
|
25755
25831
|
allowFunnelTests = false,
|
|
25756
25832
|
allowFunnelWrites = false,
|
|
@@ -25942,6 +26018,136 @@ function saasFunnelsMcpToolDefinitions({
|
|
|
25942
26018
|
}
|
|
25943
26019
|
];
|
|
25944
26020
|
if (includeHostedAccountTools) {
|
|
26021
|
+
const readAnnotations = {
|
|
26022
|
+
idempotentHint: true,
|
|
26023
|
+
openWorldHint: true,
|
|
26024
|
+
readOnlyHint: true
|
|
26025
|
+
};
|
|
26026
|
+
const windowSchema = { default: "30d", enum: resultsWindows, type: "string" };
|
|
26027
|
+
const uuidSchema = { format: "uuid", type: "string" };
|
|
26028
|
+
tools.push({
|
|
26029
|
+
annotations: readAnnotations,
|
|
26030
|
+
description: "Read bounded portfolio Results for one workspace and reporting window (30d by default). Funnel rows are paged within the first 100; stage counts, acquisition aggregates, and revenue value bases stay distinct. Check page.has_more and funnels_truncated for coverage.",
|
|
26031
|
+
inputSchema: {
|
|
26032
|
+
additionalProperties: false,
|
|
26033
|
+
properties: {
|
|
26034
|
+
attribution_role: { enum: attributionRoles, type: "string" },
|
|
26035
|
+
funnel_id: uuidSchema,
|
|
26036
|
+
limit: { type: "integer", minimum: 1, maximum: 50 },
|
|
26037
|
+
objective: { enum: ["convert", "grow", "save"], type: "string" },
|
|
26038
|
+
offset: { type: "integer", minimum: 0, maximum: 100 },
|
|
26039
|
+
window: windowSchema
|
|
26040
|
+
},
|
|
26041
|
+
type: "object"
|
|
26042
|
+
},
|
|
26043
|
+
name: "get_portfolio_results",
|
|
26044
|
+
title: "Get Portfolio Results"
|
|
26045
|
+
});
|
|
26046
|
+
tools.push({
|
|
26047
|
+
annotations: readAnnotations,
|
|
26048
|
+
description: "Read one Funnel's Results for a reporting window, including stage counts, configured acquisition aggregates, maturity, warnings, and separate revenue rows. Bounded arrays carry truncation flags.",
|
|
26049
|
+
inputSchema: {
|
|
26050
|
+
additionalProperties: false,
|
|
26051
|
+
properties: { funnel_id: uuidSchema, window: windowSchema },
|
|
26052
|
+
required: ["funnel_id"],
|
|
26053
|
+
type: "object"
|
|
26054
|
+
},
|
|
26055
|
+
name: "get_funnel_results",
|
|
26056
|
+
title: "Get Funnel Results"
|
|
26057
|
+
});
|
|
26058
|
+
tools.push({
|
|
26059
|
+
annotations: readAnnotations,
|
|
26060
|
+
description: "List up to 50 Opportunities in one workspace, optionally filtered by Funnel or lifecycle state. Returns exact total count and page.has_more; no raw evidence or monetary values.",
|
|
26061
|
+
inputSchema: {
|
|
26062
|
+
additionalProperties: false,
|
|
26063
|
+
properties: {
|
|
26064
|
+
funnel_id: uuidSchema,
|
|
26065
|
+
limit: { type: "integer", minimum: 1, maximum: 50 },
|
|
26066
|
+
offset: { type: "integer", minimum: 0, maximum: 25e4 },
|
|
26067
|
+
state: { enum: opportunityStates, type: "string" }
|
|
26068
|
+
},
|
|
26069
|
+
type: "object"
|
|
26070
|
+
},
|
|
26071
|
+
name: "list_opportunities",
|
|
26072
|
+
title: "List Opportunities"
|
|
26073
|
+
});
|
|
26074
|
+
tools.push({
|
|
26075
|
+
annotations: readAnnotations,
|
|
26076
|
+
description: "Read one Opportunity's bounded Funnel, account, lifecycle, delivery, and outcome summary in the selected workspace. No private evidence or monetary values.",
|
|
26077
|
+
inputSchema: {
|
|
26078
|
+
additionalProperties: false,
|
|
26079
|
+
properties: { opportunity_id: uuidSchema },
|
|
26080
|
+
required: ["opportunity_id"],
|
|
26081
|
+
type: "object"
|
|
26082
|
+
},
|
|
26083
|
+
name: "get_opportunity",
|
|
26084
|
+
title: "Get Opportunity"
|
|
26085
|
+
});
|
|
26086
|
+
tools.push({
|
|
26087
|
+
annotations: readAnnotations,
|
|
26088
|
+
description: "List up to 50 generated Leads in one workspace with source Funnel, lifecycle, delivery, and capture counts. Returns exact total count and page.has_more; no personal identity or raw responses.",
|
|
26089
|
+
inputSchema: {
|
|
26090
|
+
additionalProperties: false,
|
|
26091
|
+
properties: {
|
|
26092
|
+
funnel_id: uuidSchema,
|
|
26093
|
+
limit: { type: "integer", minimum: 1, maximum: 50 },
|
|
26094
|
+
offset: { type: "integer", minimum: 0, maximum: 25e4 },
|
|
26095
|
+
state: { enum: leadStates, type: "string" }
|
|
26096
|
+
},
|
|
26097
|
+
type: "object"
|
|
26098
|
+
},
|
|
26099
|
+
name: "list_leads",
|
|
26100
|
+
title: "List Leads"
|
|
26101
|
+
});
|
|
26102
|
+
tools.push({
|
|
26103
|
+
annotations: readAnnotations,
|
|
26104
|
+
description: "Read one generated Lead's bounded source, lifecycle, delivery, capture, and verified outcome summary in the selected workspace. No personal identity or raw responses.",
|
|
26105
|
+
inputSchema: {
|
|
26106
|
+
additionalProperties: false,
|
|
26107
|
+
properties: { lead_id: uuidSchema },
|
|
26108
|
+
required: ["lead_id"],
|
|
26109
|
+
type: "object"
|
|
26110
|
+
},
|
|
26111
|
+
name: "get_lead",
|
|
26112
|
+
title: "Get Lead"
|
|
26113
|
+
});
|
|
26114
|
+
tools.push({
|
|
26115
|
+
annotations: {
|
|
26116
|
+
idempotentHint: true,
|
|
26117
|
+
openWorldHint: true,
|
|
26118
|
+
readOnlyHint: true
|
|
26119
|
+
},
|
|
26120
|
+
description: "List one selected workspace's Funnels with lifecycle, draft, and publication state. Results include evaluation time, total count, and pagination; use offset to read more pages. No Funnel definitions or customer data.",
|
|
26121
|
+
inputSchema: {
|
|
26122
|
+
additionalProperties: false,
|
|
26123
|
+
properties: {
|
|
26124
|
+
include_archived: { type: "boolean" },
|
|
26125
|
+
limit: { type: "integer", minimum: 1, maximum: 50 },
|
|
26126
|
+
offset: { type: "integer", minimum: 0 }
|
|
26127
|
+
},
|
|
26128
|
+
type: "object"
|
|
26129
|
+
},
|
|
26130
|
+
name: "list_funnels",
|
|
26131
|
+
title: "List Funnels"
|
|
26132
|
+
});
|
|
26133
|
+
tools.push({
|
|
26134
|
+
annotations: {
|
|
26135
|
+
idempotentHint: true,
|
|
26136
|
+
openWorldHint: true,
|
|
26137
|
+
readOnlyHint: true
|
|
26138
|
+
},
|
|
26139
|
+
description: "Read one Funnel's current lifecycle, draft and published revisions, and server-derived publication readiness in the selected workspace. No definitions or private evidence.",
|
|
26140
|
+
inputSchema: {
|
|
26141
|
+
additionalProperties: false,
|
|
26142
|
+
properties: {
|
|
26143
|
+
funnel_id: { type: "string", format: "uuid" }
|
|
26144
|
+
},
|
|
26145
|
+
required: ["funnel_id"],
|
|
26146
|
+
type: "object"
|
|
26147
|
+
},
|
|
26148
|
+
name: "get_funnel_state",
|
|
26149
|
+
title: "Get Funnel State"
|
|
26150
|
+
});
|
|
25945
26151
|
tools.push({
|
|
25946
26152
|
annotations: {
|
|
25947
26153
|
idempotentHint: true,
|
|
@@ -26128,7 +26334,7 @@ async function developerGet(path, args, options) {
|
|
|
26128
26334
|
});
|
|
26129
26335
|
}
|
|
26130
26336
|
const response = await (options.fetch ?? fetch)(
|
|
26131
|
-
`${apiBaseUrl(options, args)}${path}`,
|
|
26337
|
+
`${apiBaseUrl(options, args)}${developerPathForWorkspace(path, args, options)}`,
|
|
26132
26338
|
{
|
|
26133
26339
|
headers: {
|
|
26134
26340
|
authorization
|
|
@@ -26164,7 +26370,7 @@ async function developerPost(path, args, body, options, requiredScope) {
|
|
|
26164
26370
|
});
|
|
26165
26371
|
}
|
|
26166
26372
|
const response = await (options.fetch ?? fetch)(
|
|
26167
|
-
`${apiBaseUrl(options, args)}${path}`,
|
|
26373
|
+
`${apiBaseUrl(options, args)}${developerPathForWorkspace(path, args, options)}`,
|
|
26168
26374
|
{
|
|
26169
26375
|
body: JSON.stringify(body),
|
|
26170
26376
|
headers: {
|
|
@@ -26234,11 +26440,17 @@ async function sendTestEvent(args, options) {
|
|
|
26234
26440
|
}
|
|
26235
26441
|
async function callSaaSFunnelsMcpTool(name, args, options = {}) {
|
|
26236
26442
|
try {
|
|
26237
|
-
if (
|
|
26443
|
+
if (!options.hostedMode && "workspace_id" in args && hostedWorkspaceToolNames.has(name)) {
|
|
26444
|
+
throw new McpProtocolError(
|
|
26445
|
+
-32602,
|
|
26446
|
+
"workspace_id is available only with hosted OAuth; an API key is pinned to its workspace."
|
|
26447
|
+
);
|
|
26448
|
+
}
|
|
26449
|
+
if ((name === "get_account_strategy" || name === "get_funnel_results" || name === "get_funnel_state" || name === "get_lead" || name === "get_opportunity" || name === "get_portfolio_results" || name === "list_funnels" || name === "list_leads" || name === "list_opportunities" || name === "simulate_account_funnel_fit" || name === "list_workspaces") && !options.hostedMode) {
|
|
26238
26450
|
throw new McpToolExecutionError(
|
|
26239
|
-
"This
|
|
26451
|
+
"This tool requires hosted OAuth.",
|
|
26240
26452
|
{
|
|
26241
|
-
error: "This
|
|
26453
|
+
error: "This tool requires hosted OAuth.",
|
|
26242
26454
|
ok: false,
|
|
26243
26455
|
required_boundary: "hosted_mcp"
|
|
26244
26456
|
}
|
|
@@ -26281,6 +26493,106 @@ async function callSaaSFunnelsMcpTool(name, args, options = {}) {
|
|
|
26281
26493
|
options
|
|
26282
26494
|
)
|
|
26283
26495
|
);
|
|
26496
|
+
if (name === "list_workspaces")
|
|
26497
|
+
return structuredToolResult(
|
|
26498
|
+
await developerGet("/api/developer-tools/workspaces", {}, options)
|
|
26499
|
+
);
|
|
26500
|
+
if (name === "list_funnels") {
|
|
26501
|
+
const limit = args.limit ?? 25;
|
|
26502
|
+
const offset = args.offset ?? 0;
|
|
26503
|
+
if (!Number.isInteger(limit) || Number(limit) < 1 || Number(limit) > 50 || !Number.isInteger(offset) || Number(offset) < 0 || Number(offset) > 25e4 || args.include_archived !== void 0 && typeof args.include_archived !== "boolean") {
|
|
26504
|
+
throw new McpProtocolError(
|
|
26505
|
+
-32602,
|
|
26506
|
+
"limit must be 1\u201350, offset must be 0\u2013250000, and include_archived must be boolean."
|
|
26507
|
+
);
|
|
26508
|
+
}
|
|
26509
|
+
const query = new URLSearchParams({
|
|
26510
|
+
include_archived: String(args.include_archived === true),
|
|
26511
|
+
limit: String(limit),
|
|
26512
|
+
offset: String(offset)
|
|
26513
|
+
});
|
|
26514
|
+
return structuredToolResult(
|
|
26515
|
+
await developerGet(`/api/developer-tools/funnels?${query}`, args, options)
|
|
26516
|
+
);
|
|
26517
|
+
}
|
|
26518
|
+
if (name === "get_funnel_state") {
|
|
26519
|
+
const funnelId = uuidArg(args, "funnel_id", true);
|
|
26520
|
+
return structuredToolResult(
|
|
26521
|
+
await developerGet(
|
|
26522
|
+
`/api/developer-tools/funnels/${encodeURIComponent(funnelId)}`,
|
|
26523
|
+
args,
|
|
26524
|
+
options
|
|
26525
|
+
)
|
|
26526
|
+
);
|
|
26527
|
+
}
|
|
26528
|
+
if (name === "get_portfolio_results") {
|
|
26529
|
+
const query = new URLSearchParams({
|
|
26530
|
+
window: resultsWindowArg(args),
|
|
26531
|
+
limit: String(integerArg(args, "limit", 50, 50)),
|
|
26532
|
+
offset: String(integerArg(args, "offset", 0, 100))
|
|
26533
|
+
});
|
|
26534
|
+
const funnelId = uuidArg(args, "funnel_id");
|
|
26535
|
+
if (funnelId) query.set("funnel_id", funnelId);
|
|
26536
|
+
const objective = optionalFilterArg(args, "objective");
|
|
26537
|
+
if (objective) {
|
|
26538
|
+
if (!["convert", "grow", "save"].includes(objective)) {
|
|
26539
|
+
throw new McpProtocolError(-32602, "objective must be convert, grow, or save.");
|
|
26540
|
+
}
|
|
26541
|
+
query.set("objective", objective);
|
|
26542
|
+
}
|
|
26543
|
+
const attributionRole = optionalFilterArg(args, "attribution_role");
|
|
26544
|
+
if (attributionRole) {
|
|
26545
|
+
if (!attributionRoles.includes(attributionRole)) {
|
|
26546
|
+
throw new McpProtocolError(-32602, "attribution_role must be direct, influenced, or observed.");
|
|
26547
|
+
}
|
|
26548
|
+
query.set("attribution_role", attributionRole);
|
|
26549
|
+
}
|
|
26550
|
+
return structuredToolResult(
|
|
26551
|
+
await developerGet(`/api/developer-tools/results?${query}`, args, options)
|
|
26552
|
+
);
|
|
26553
|
+
}
|
|
26554
|
+
if (name === "get_funnel_results") {
|
|
26555
|
+
const funnelId = uuidArg(args, "funnel_id", true);
|
|
26556
|
+
const query = new URLSearchParams({ window: resultsWindowArg(args) });
|
|
26557
|
+
return structuredToolResult(
|
|
26558
|
+
await developerGet(
|
|
26559
|
+
`/api/developer-tools/funnels/${encodeURIComponent(funnelId)}/results?${query}`,
|
|
26560
|
+
args,
|
|
26561
|
+
options
|
|
26562
|
+
)
|
|
26563
|
+
);
|
|
26564
|
+
}
|
|
26565
|
+
if (name === "list_opportunities" || name === "list_leads") {
|
|
26566
|
+
const query = new URLSearchParams({
|
|
26567
|
+
limit: String(integerArg(args, "limit", 25, 50)),
|
|
26568
|
+
offset: String(integerArg(args, "offset", 0, 25e4))
|
|
26569
|
+
});
|
|
26570
|
+
const funnelId = uuidArg(args, "funnel_id");
|
|
26571
|
+
if (funnelId) query.set("funnel_id", funnelId);
|
|
26572
|
+
const state = optionalFilterArg(args, "state");
|
|
26573
|
+
if (state) {
|
|
26574
|
+
const allowed3 = name === "list_leads" ? leadStates : opportunityStates;
|
|
26575
|
+
if (!allowed3.includes(state)) {
|
|
26576
|
+
throw new McpProtocolError(-32602, `state is not a supported ${name === "list_leads" ? "Lead" : "Opportunity"} state.`);
|
|
26577
|
+
}
|
|
26578
|
+
query.set("state", state);
|
|
26579
|
+
}
|
|
26580
|
+
const resource = name === "list_leads" ? "leads" : "opportunities";
|
|
26581
|
+
return structuredToolResult(
|
|
26582
|
+
await developerGet(`/api/developer-tools/${resource}?${query}`, args, options)
|
|
26583
|
+
);
|
|
26584
|
+
}
|
|
26585
|
+
if (name === "get_opportunity" || name === "get_lead") {
|
|
26586
|
+
const resource = name === "get_lead" ? "leads" : "opportunities";
|
|
26587
|
+
const id = uuidArg(args, name === "get_lead" ? "lead_id" : "opportunity_id", true);
|
|
26588
|
+
return structuredToolResult(
|
|
26589
|
+
await developerGet(
|
|
26590
|
+
`/api/developer-tools/${resource}/${encodeURIComponent(id)}`,
|
|
26591
|
+
args,
|
|
26592
|
+
options
|
|
26593
|
+
)
|
|
26594
|
+
);
|
|
26595
|
+
}
|
|
26284
26596
|
if (name === "get_workspace_readiness")
|
|
26285
26597
|
return structuredToolResult(
|
|
26286
26598
|
await developerGet(
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export type AgentCapabilityId = "account.funnel_fit.simulate" | "account.strategy.read" | "developer.event.validate" | "developer.feature_manifest.validate" | "developer.handoff.generate" | "developer.workspace.read" | "funnel.entry.apply" | "funnel.entry.evidence.submit" | "funnel.entry.inspect" | "funnel.entry.plan" | "funnel.entry.test" | "setup.test_event.send";
|
|
1
|
+
export type AgentCapabilityId = "account.funnel_fit.simulate" | "account.strategy.read" | "developer.event.validate" | "developer.feature_manifest.validate" | "developer.handoff.generate" | "developer.workspace.read" | "funnel.entry.apply" | "funnel.entry.evidence.submit" | "funnel.entry.inspect" | "funnel.entry.plan" | "funnel.entry.test" | "funnel.workspace.read" | "setup.test_event.send";
|
|
2
2
|
type AgentExecutionBoundary = "api_key" | "hosted_mcp" | "in_app" | "local_cli" | "local_mcp" | "scheduled_agent";
|
|
3
3
|
type AgentOperationClass = "external_write" | "plan" | "prepare" | "read" | "reversible_write" | "simulate" | "validate";
|
|
4
4
|
type Capability = {
|
package/dist/types/identity.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export declare const SAASFUNNELS_CLI_NAME = "saasfunnels";
|
|
2
|
-
export declare const SAASFUNNELS_CLI_VERSION = "0.2.
|
|
2
|
+
export declare const SAASFUNNELS_CLI_VERSION = "0.2.3";
|
|
3
3
|
export declare const SAASFUNNELS_PRODUCT_NAME = "SaaSFunnels";
|
|
4
4
|
export declare const SAASFUNNELS_DEFAULT_API_BASE_URL = "https://app.saasfunnels.ai";
|
|
5
5
|
export declare const SAASFUNNELS_ENV: Readonly<{
|
package/dist/types/mcp.d.ts
CHANGED
|
@@ -35,10 +35,19 @@ export declare const saasFunnelsMcpToolCapabilityIds: {
|
|
|
35
35
|
readonly apply_funnel_entry: "funnel.entry.apply";
|
|
36
36
|
readonly generate_direct_api_handoff: "developer.handoff.generate";
|
|
37
37
|
readonly get_integration_health: "developer.workspace.read";
|
|
38
|
+
readonly get_funnel_results: "funnel.workspace.read";
|
|
39
|
+
readonly get_funnel_state: "funnel.workspace.read";
|
|
40
|
+
readonly get_lead: "funnel.workspace.read";
|
|
41
|
+
readonly get_opportunity: "funnel.workspace.read";
|
|
42
|
+
readonly get_portfolio_results: "funnel.workspace.read";
|
|
38
43
|
readonly get_signal_payload_preview: "developer.workspace.read";
|
|
39
44
|
readonly get_workspace_readiness: "developer.workspace.read";
|
|
45
|
+
readonly list_workspaces: "developer.workspace.read";
|
|
40
46
|
readonly inspect_funnel_entry: "funnel.entry.inspect";
|
|
47
|
+
readonly list_funnels: "funnel.workspace.read";
|
|
48
|
+
readonly list_leads: "funnel.workspace.read";
|
|
41
49
|
readonly list_mapping_gaps: "developer.workspace.read";
|
|
50
|
+
readonly list_opportunities: "funnel.workspace.read";
|
|
42
51
|
readonly list_recent_signals: "developer.workspace.read";
|
|
43
52
|
readonly simulate_account_funnel_fit: "account.funnel_fit.simulate";
|
|
44
53
|
readonly propose_funnel_entry_installation: "funnel.entry.plan";
|
|
@@ -68,7 +77,7 @@ export type SaaSFunnelsMcpOptions = {
|
|
|
68
77
|
output?: Pick<NodeJS.WritableStream, "write">;
|
|
69
78
|
};
|
|
70
79
|
declare const mcpProtocolVersion = "2025-06-18";
|
|
71
|
-
declare const mcpServerVersion = "0.2.
|
|
80
|
+
declare const mcpServerVersion = "0.2.3";
|
|
72
81
|
export declare function saasFunnelsMcpResources(): McpResourceDefinition[];
|
|
73
82
|
export declare function readSaaSFunnelsMcpResource(uri: string, options?: SaaSFunnelsMcpOptions): string;
|
|
74
83
|
export declare function saasFunnelsMcpToolDefinitions({ allowFunnelTests, allowFunnelWrites, allowTestWrites, includeHostedAccountTools, }?: {
|