Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .claude-plugin/marketplace.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
"name": "genesys-cloud-architect",
"source": "./",
"description": "Create, test, and debug Genesys Cloud Architect flows",
"version": "2.0.3"
"version": "2.0.4"
}
]
}
2 changes: 1 addition & 1 deletion .claude-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "genesys-cloud-architect",
"description": "Create, test, and debug Genesys Cloud Architect flows",
"version": "2.0.3",
"version": "2.0.4",
"author": {
"name": "Lucas Woodward",
"url": "https://makingchatbots.com/"
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "genesys-cloud-architect",
"version": "2.0.3",
"version": "2.0.4",
"private": true,
"packageManager": "pnpm@11.1.3+sha512.c85357fe17ca12dd23dd7071822666dfd7e3cb76fe214e3370b5ea2fb34f2a231185509b63e717f3cd0acb38dd3f8d82bcd5e8172400ae678b70ea4fbed0896d",
"author": {
Expand Down
40 changes: 20 additions & 20 deletions servers/genesys-cloud-architect-mcp.js

Large diffs are not rendered by default.

46 changes: 36 additions & 10 deletions src/mcp-server/tools/find-flow.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,25 @@ interface FindFlowResult {
notes?: string[];
}

/**
* The Architect flows API splits the name filter on whitespace and requires
* every word to match a whole token of the flow name, ANDed in any order.
* Underscores do not split tokens, so a bare "Returns" misses
* "UK_Returns_Desk Main Flow" and "Order Status" misses "Order_Status". Asterisk
* wildcards are honoured inside a word, so wrap each word in them to get
* "every word appears somewhere in the name" semantics. Wrapping the whole
* query is not enough: "*Order Status*" becomes "*Order" and "Status*", which
* both fail against the single token "Order_Status". Any asterisks the caller
* already added are stripped so they aren't doubled up.
*/
export function toWildcardName(name: string): string {
return name
.trim()
.split(/\s+/)
.map((word) => `*${word.replace(/^\*+|\*+$/g, "")}*`)
.join(" ");
}

function toFlowSummary(flow: platformClient.Models.Flow): FlowSummary {
return {
id: flow.id ?? "",
Expand All @@ -48,9 +67,12 @@ const inputSchema = {
.string()
.min(1)
.describe(
"The flow name, or a portion of it, to search for. Matched " +
"case-insensitively against flow names, so a partial name " +
"like 'payment' finds 'Book_Payment'.",
"The flow name, or some words from it, to search for. Every " +
"word must appear somewhere in the flow name, " +
"case-insensitively and in any order, so 'payment' finds " +
"'Book_Payment' and 'Returns' finds 'UK_Returns_Desk Main Flow'. " +
"Wildcards are added automatically; do not include " +
"asterisks.",
),
type: z
.array(z.string())
Expand All @@ -71,9 +93,11 @@ export const findFlow: ToolFactory<ToolConfig, typeof inputSchema> = ({
"human-readable name users know (e.g. 'Book_Payment') to the flow " +
"id every other flow tool requires. Returns each matching flow's " +
"id, name, type and published version. Matching is a " +
"case-insensitive search over flow names that accepts a portion " +
"of the name; flows whose name equals the query exactly are " +
"listed first. The returned id is accepted verbatim by flow_ir, " +
"case-insensitive search over flow names in which every word of " +
"the query must appear somewhere in the name, in any order, so " +
"a fragment of a word or a few words from the name both work; " +
"flows whose name equals the query exactly are listed first. " +
"The returned id is accepted verbatim by flow_ir, " +
"flow_action, search_in_flow and flow_dependencies. A " +
"publishedVersion of null means the flow has never been " +
"published.",
Expand All @@ -87,12 +111,13 @@ export const findFlow: ToolFactory<ToolConfig, typeof inputSchema> = ({
handler: async ({ name, type }) => {
try {
const flows: platformClient.Models.Flow[] = [];
const wildcardName = toWildcardName(name);

let total = 0;
let pageNumber = 1;
while (true) {
const page = await architectApi.getFlows({
name,
name: wildcardName,
pageSize: 100,
pageNumber,
...(type?.length ? { type } : {}),
Expand All @@ -119,9 +144,10 @@ export const findFlow: ToolFactory<ToolConfig, typeof inputSchema> = ({
const notes: string[] = [];
if (returned.length === 0) {
notes.push(
`No flows matched "${name}". The search already accepts ` +
"a portion of the name, so try a shorter fragment, " +
"or drop the type filter if one was given.",
`No flows matched "${name}". Every word of the query ` +
"must appear in the flow name, so drop words you " +
"are unsure of (keep the most distinctive one), or " +
"drop the type filter if one was given.",
);
} else if (total > returned.length) {
// Only claim exact-first ordering when an exact match was
Expand Down
10 changes: 6 additions & 4 deletions src/mcp-server/tools/find-queue.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,12 @@ interface FindQueueResult {
}

/**
* The Routing API's name filter is an exact match unless the value carries
* leading/trailing asterisks, unlike the Architect flows API which matches a
* fragment natively. Wrap the fragment so both find_* tools behave alike, and
* strip any asterisks the caller already added so they aren't doubled up.
* The Routing API's name filter is an exact match on the whole string unless
* the value carries leading/trailing asterisks. Wrap the fragment so a
* portion of the name matches, and strip any asterisks the caller already
* added so they aren't doubled up. (The Architect flows API tokenises its
* filter differently, so find_flow wraps each word instead; see its
* toWildcardName.)
*/
function toWildcardName(name: string): string {
const fragment = name.trim().replace(/^\*+|\*+$/g, "");
Expand Down
Loading