saasfunnels 0.2.1 → 0.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -0
- package/dist/library.js +60 -7
- package/dist/saasfunnels.js +37 -7
- package/dist/types/identity.d.ts +1 -1
- package/dist/types/mcp.d.ts +2 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -73,6 +73,8 @@ 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
|
+
|
|
76
78
|
## Development
|
|
77
79
|
|
|
78
80
|
```bash
|
package/dist/library.js
CHANGED
|
@@ -24260,7 +24260,7 @@ function validateFeatureCatalogManifest(value) {
|
|
|
24260
24260
|
|
|
24261
24261
|
// src/identity.ts
|
|
24262
24262
|
var SAASFUNNELS_CLI_NAME = "saasfunnels";
|
|
24263
|
-
var SAASFUNNELS_CLI_VERSION = "0.2.
|
|
24263
|
+
var SAASFUNNELS_CLI_VERSION = "0.2.2";
|
|
24264
24264
|
var SAASFUNNELS_PRODUCT_NAME = "SaaSFunnels";
|
|
24265
24265
|
var SAASFUNNELS_DEFAULT_API_BASE_URL = "https://app.saasfunnels.ai";
|
|
24266
24266
|
var SAASFUNNELS_ENV = Object.freeze({
|
|
@@ -24299,6 +24299,7 @@ var saasFunnelsMcpToolCapabilityIds = {
|
|
|
24299
24299
|
get_integration_health: "developer.workspace.read",
|
|
24300
24300
|
get_signal_payload_preview: "developer.workspace.read",
|
|
24301
24301
|
get_workspace_readiness: "developer.workspace.read",
|
|
24302
|
+
list_workspaces: "developer.workspace.read",
|
|
24302
24303
|
inspect_funnel_entry: "funnel.entry.inspect",
|
|
24303
24304
|
list_mapping_gaps: "developer.workspace.read",
|
|
24304
24305
|
list_recent_signals: "developer.workspace.read",
|
|
@@ -24399,7 +24400,7 @@ function objectArg(args, name) {
|
|
|
24399
24400
|
function sanitizeString(value) {
|
|
24400
24401
|
const withoutSecrets = value.replace(
|
|
24401
24402
|
tokenPattern,
|
|
24402
|
-
(candidate) => /^[A-Z0-9_]+$/.test(candidate) ? candidate : "[redacted-token]"
|
|
24403
|
+
(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
24404
|
).replace(emailPattern3, "[redacted-email]");
|
|
24404
24405
|
try {
|
|
24405
24406
|
const parsed = new URL(
|
|
@@ -24668,6 +24669,25 @@ function emptyLiveInputSchema() {
|
|
|
24668
24669
|
type: "object"
|
|
24669
24670
|
};
|
|
24670
24671
|
}
|
|
24672
|
+
var hostedWorkspaceToolNames = /* @__PURE__ */ new Set([
|
|
24673
|
+
"get_integration_health",
|
|
24674
|
+
"get_workspace_readiness",
|
|
24675
|
+
"list_mapping_gaps",
|
|
24676
|
+
"list_recent_signals",
|
|
24677
|
+
"get_signal_payload_preview",
|
|
24678
|
+
"inspect_funnel_entry",
|
|
24679
|
+
"propose_funnel_entry_installation",
|
|
24680
|
+
"get_account_strategy",
|
|
24681
|
+
"simulate_account_funnel_fit"
|
|
24682
|
+
]);
|
|
24683
|
+
function developerPathForWorkspace(path, args, options) {
|
|
24684
|
+
if (!options.hostedMode) return path;
|
|
24685
|
+
if ("workspace_id" in args && !stringArg(args, "workspace_id")) {
|
|
24686
|
+
throw new McpProtocolError(-32602, "workspace_id must be a non-empty string.");
|
|
24687
|
+
}
|
|
24688
|
+
const workspaceId = stringArg(args, "workspace_id");
|
|
24689
|
+
return workspaceId ? `${path}${path.includes("?") ? "&" : "?"}workspace_id=${encodeURIComponent(workspaceId)}` : path;
|
|
24690
|
+
}
|
|
24671
24691
|
function saasFunnelsMcpToolDefinitions({
|
|
24672
24692
|
allowFunnelTests = false,
|
|
24673
24693
|
allowFunnelWrites = false,
|
|
@@ -25030,6 +25050,29 @@ function hostedSaaSFunnelsMcpToolDefinitions() {
|
|
|
25030
25050
|
capability2 && agentCapabilityAllowedAtBoundary(capability2.id, "hosted_mcp")
|
|
25031
25051
|
);
|
|
25032
25052
|
});
|
|
25053
|
+
for (const tool of tools) {
|
|
25054
|
+
if (!hostedWorkspaceToolNames.has(tool.name)) continue;
|
|
25055
|
+
const properties = tool.inputSchema.properties;
|
|
25056
|
+
properties.workspace_id = {
|
|
25057
|
+
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.",
|
|
25058
|
+
type: "string"
|
|
25059
|
+
};
|
|
25060
|
+
}
|
|
25061
|
+
tools.push({
|
|
25062
|
+
annotations: {
|
|
25063
|
+
idempotentHint: true,
|
|
25064
|
+
openWorldHint: true,
|
|
25065
|
+
readOnlyHint: true
|
|
25066
|
+
},
|
|
25067
|
+
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.",
|
|
25068
|
+
inputSchema: {
|
|
25069
|
+
additionalProperties: false,
|
|
25070
|
+
properties: {},
|
|
25071
|
+
type: "object"
|
|
25072
|
+
},
|
|
25073
|
+
name: "list_workspaces",
|
|
25074
|
+
title: "List Workspaces"
|
|
25075
|
+
});
|
|
25033
25076
|
assertMcpToolCapabilityContract(tools, "hosted_mcp");
|
|
25034
25077
|
return tools;
|
|
25035
25078
|
}
|
|
@@ -25055,7 +25098,7 @@ async function developerGet(path, args, options) {
|
|
|
25055
25098
|
});
|
|
25056
25099
|
}
|
|
25057
25100
|
const response = await (options.fetch ?? fetch)(
|
|
25058
|
-
`${apiBaseUrl(options, args)}${path}`,
|
|
25101
|
+
`${apiBaseUrl(options, args)}${developerPathForWorkspace(path, args, options)}`,
|
|
25059
25102
|
{
|
|
25060
25103
|
headers: {
|
|
25061
25104
|
authorization
|
|
@@ -25091,7 +25134,7 @@ async function developerPost(path, args, body, options, requiredScope) {
|
|
|
25091
25134
|
});
|
|
25092
25135
|
}
|
|
25093
25136
|
const response = await (options.fetch ?? fetch)(
|
|
25094
|
-
`${apiBaseUrl(options, args)}${path}`,
|
|
25137
|
+
`${apiBaseUrl(options, args)}${developerPathForWorkspace(path, args, options)}`,
|
|
25095
25138
|
{
|
|
25096
25139
|
body: JSON.stringify(body),
|
|
25097
25140
|
headers: {
|
|
@@ -25161,11 +25204,17 @@ async function sendTestEvent(args, options) {
|
|
|
25161
25204
|
}
|
|
25162
25205
|
async function callSaaSFunnelsMcpTool(name, args, options = {}) {
|
|
25163
25206
|
try {
|
|
25164
|
-
if (
|
|
25207
|
+
if (!options.hostedMode && "workspace_id" in args && hostedWorkspaceToolNames.has(name)) {
|
|
25208
|
+
throw new McpProtocolError(
|
|
25209
|
+
-32602,
|
|
25210
|
+
"workspace_id is available only with hosted OAuth; an API key is pinned to its workspace."
|
|
25211
|
+
);
|
|
25212
|
+
}
|
|
25213
|
+
if ((name === "get_account_strategy" || name === "simulate_account_funnel_fit" || name === "list_workspaces") && !options.hostedMode) {
|
|
25165
25214
|
throw new McpToolExecutionError(
|
|
25166
|
-
"This
|
|
25215
|
+
"This tool requires hosted OAuth.",
|
|
25167
25216
|
{
|
|
25168
|
-
error: "This
|
|
25217
|
+
error: "This tool requires hosted OAuth.",
|
|
25169
25218
|
ok: false,
|
|
25170
25219
|
required_boundary: "hosted_mcp"
|
|
25171
25220
|
}
|
|
@@ -25208,6 +25257,10 @@ async function callSaaSFunnelsMcpTool(name, args, options = {}) {
|
|
|
25208
25257
|
options
|
|
25209
25258
|
)
|
|
25210
25259
|
);
|
|
25260
|
+
if (name === "list_workspaces")
|
|
25261
|
+
return structuredToolResult(
|
|
25262
|
+
await developerGet("/api/developer-tools/workspaces", {}, options)
|
|
25263
|
+
);
|
|
25211
25264
|
if (name === "get_workspace_readiness")
|
|
25212
25265
|
return structuredToolResult(
|
|
25213
25266
|
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.2";
|
|
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({
|
|
@@ -25382,6 +25382,7 @@ var saasFunnelsMcpToolCapabilityIds = {
|
|
|
25382
25382
|
get_integration_health: "developer.workspace.read",
|
|
25383
25383
|
get_signal_payload_preview: "developer.workspace.read",
|
|
25384
25384
|
get_workspace_readiness: "developer.workspace.read",
|
|
25385
|
+
list_workspaces: "developer.workspace.read",
|
|
25385
25386
|
inspect_funnel_entry: "funnel.entry.inspect",
|
|
25386
25387
|
list_mapping_gaps: "developer.workspace.read",
|
|
25387
25388
|
list_recent_signals: "developer.workspace.read",
|
|
@@ -25482,7 +25483,7 @@ function objectArg(args, name) {
|
|
|
25482
25483
|
function sanitizeString(value) {
|
|
25483
25484
|
const withoutSecrets = value.replace(
|
|
25484
25485
|
tokenPattern,
|
|
25485
|
-
(candidate) => /^[A-Z0-9_]+$/.test(candidate) ? candidate : "[redacted-token]"
|
|
25486
|
+
(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
25487
|
).replace(emailPattern3, "[redacted-email]");
|
|
25487
25488
|
try {
|
|
25488
25489
|
const parsed = new URL(
|
|
@@ -25751,6 +25752,25 @@ function emptyLiveInputSchema() {
|
|
|
25751
25752
|
type: "object"
|
|
25752
25753
|
};
|
|
25753
25754
|
}
|
|
25755
|
+
var hostedWorkspaceToolNames = /* @__PURE__ */ new Set([
|
|
25756
|
+
"get_integration_health",
|
|
25757
|
+
"get_workspace_readiness",
|
|
25758
|
+
"list_mapping_gaps",
|
|
25759
|
+
"list_recent_signals",
|
|
25760
|
+
"get_signal_payload_preview",
|
|
25761
|
+
"inspect_funnel_entry",
|
|
25762
|
+
"propose_funnel_entry_installation",
|
|
25763
|
+
"get_account_strategy",
|
|
25764
|
+
"simulate_account_funnel_fit"
|
|
25765
|
+
]);
|
|
25766
|
+
function developerPathForWorkspace(path, args, options) {
|
|
25767
|
+
if (!options.hostedMode) return path;
|
|
25768
|
+
if ("workspace_id" in args && !stringArg(args, "workspace_id")) {
|
|
25769
|
+
throw new McpProtocolError(-32602, "workspace_id must be a non-empty string.");
|
|
25770
|
+
}
|
|
25771
|
+
const workspaceId = stringArg(args, "workspace_id");
|
|
25772
|
+
return workspaceId ? `${path}${path.includes("?") ? "&" : "?"}workspace_id=${encodeURIComponent(workspaceId)}` : path;
|
|
25773
|
+
}
|
|
25754
25774
|
function saasFunnelsMcpToolDefinitions({
|
|
25755
25775
|
allowFunnelTests = false,
|
|
25756
25776
|
allowFunnelWrites = false,
|
|
@@ -26128,7 +26148,7 @@ async function developerGet(path, args, options) {
|
|
|
26128
26148
|
});
|
|
26129
26149
|
}
|
|
26130
26150
|
const response = await (options.fetch ?? fetch)(
|
|
26131
|
-
`${apiBaseUrl(options, args)}${path}`,
|
|
26151
|
+
`${apiBaseUrl(options, args)}${developerPathForWorkspace(path, args, options)}`,
|
|
26132
26152
|
{
|
|
26133
26153
|
headers: {
|
|
26134
26154
|
authorization
|
|
@@ -26164,7 +26184,7 @@ async function developerPost(path, args, body, options, requiredScope) {
|
|
|
26164
26184
|
});
|
|
26165
26185
|
}
|
|
26166
26186
|
const response = await (options.fetch ?? fetch)(
|
|
26167
|
-
`${apiBaseUrl(options, args)}${path}`,
|
|
26187
|
+
`${apiBaseUrl(options, args)}${developerPathForWorkspace(path, args, options)}`,
|
|
26168
26188
|
{
|
|
26169
26189
|
body: JSON.stringify(body),
|
|
26170
26190
|
headers: {
|
|
@@ -26234,11 +26254,17 @@ async function sendTestEvent(args, options) {
|
|
|
26234
26254
|
}
|
|
26235
26255
|
async function callSaaSFunnelsMcpTool(name, args, options = {}) {
|
|
26236
26256
|
try {
|
|
26237
|
-
if (
|
|
26257
|
+
if (!options.hostedMode && "workspace_id" in args && hostedWorkspaceToolNames.has(name)) {
|
|
26258
|
+
throw new McpProtocolError(
|
|
26259
|
+
-32602,
|
|
26260
|
+
"workspace_id is available only with hosted OAuth; an API key is pinned to its workspace."
|
|
26261
|
+
);
|
|
26262
|
+
}
|
|
26263
|
+
if ((name === "get_account_strategy" || name === "simulate_account_funnel_fit" || name === "list_workspaces") && !options.hostedMode) {
|
|
26238
26264
|
throw new McpToolExecutionError(
|
|
26239
|
-
"This
|
|
26265
|
+
"This tool requires hosted OAuth.",
|
|
26240
26266
|
{
|
|
26241
|
-
error: "This
|
|
26267
|
+
error: "This tool requires hosted OAuth.",
|
|
26242
26268
|
ok: false,
|
|
26243
26269
|
required_boundary: "hosted_mcp"
|
|
26244
26270
|
}
|
|
@@ -26281,6 +26307,10 @@ async function callSaaSFunnelsMcpTool(name, args, options = {}) {
|
|
|
26281
26307
|
options
|
|
26282
26308
|
)
|
|
26283
26309
|
);
|
|
26310
|
+
if (name === "list_workspaces")
|
|
26311
|
+
return structuredToolResult(
|
|
26312
|
+
await developerGet("/api/developer-tools/workspaces", {}, options)
|
|
26313
|
+
);
|
|
26284
26314
|
if (name === "get_workspace_readiness")
|
|
26285
26315
|
return structuredToolResult(
|
|
26286
26316
|
await developerGet(
|
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.2";
|
|
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
|
@@ -37,6 +37,7 @@ export declare const saasFunnelsMcpToolCapabilityIds: {
|
|
|
37
37
|
readonly get_integration_health: "developer.workspace.read";
|
|
38
38
|
readonly get_signal_payload_preview: "developer.workspace.read";
|
|
39
39
|
readonly get_workspace_readiness: "developer.workspace.read";
|
|
40
|
+
readonly list_workspaces: "developer.workspace.read";
|
|
40
41
|
readonly inspect_funnel_entry: "funnel.entry.inspect";
|
|
41
42
|
readonly list_mapping_gaps: "developer.workspace.read";
|
|
42
43
|
readonly list_recent_signals: "developer.workspace.read";
|
|
@@ -68,7 +69,7 @@ export type SaaSFunnelsMcpOptions = {
|
|
|
68
69
|
output?: Pick<NodeJS.WritableStream, "write">;
|
|
69
70
|
};
|
|
70
71
|
declare const mcpProtocolVersion = "2025-06-18";
|
|
71
|
-
declare const mcpServerVersion = "0.2.
|
|
72
|
+
declare const mcpServerVersion = "0.2.2";
|
|
72
73
|
export declare function saasFunnelsMcpResources(): McpResourceDefinition[];
|
|
73
74
|
export declare function readSaaSFunnelsMcpResource(uri: string, options?: SaaSFunnelsMcpOptions): string;
|
|
74
75
|
export declare function saasFunnelsMcpToolDefinitions({ allowFunnelTests, allowFunnelWrites, allowTestWrites, includeHostedAccountTools, }?: {
|