diff --git a/platform/backend/src/archestra-mcp-server.test.ts b/platform/backend/src/archestra-mcp-server.test.ts index ba76cf364c..f8a2ed0c3f 100644 --- a/platform/backend/src/archestra-mcp-server.test.ts +++ b/platform/backend/src/archestra-mcp-server.test.ts @@ -163,6 +163,7 @@ describe("executeArchestraTool", () => { id: testAgent.id, name: testAgent.name, }, + userId: testAgent.authorId ?? undefined, }; }); @@ -1351,6 +1352,100 @@ describe("executeArchestraTool", () => { }); }); }); + + describe("get_* tools", () => { + test("should fall back to name search when get_mcp_gateway receives a name in id", async ({ + makeAgent, + }) => { + const gateway = await makeAgent({ + name: "Grafana Export Gateway", + agentType: "mcp_gateway", + scope: "personal", + }); + + const result = await executeArchestraTool( + `${ARCHESTRA_MCP_SERVER_NAME}${MCP_SERVER_TOOL_NAME_SEPARATOR}get_mcp_gateway`, + { id: gateway.name }, + { + ...mockContext, + userId: gateway.authorId ?? mockContext.userId, + }, + ); + + expect(result.isError).toBe(false); + expect((result.content[0] as any).text).toContain(gateway.id); + expect((result.content[0] as any).text).toContain(gateway.name); + }); + + test("should fall back to name search when get_agent receives a name in id", async ({ + makeAgent, + }) => { + const agent = await makeAgent({ + name: "Prompt Router Agent", + agentType: "agent", + scope: "personal", + }); + + const result = await executeArchestraTool( + `${ARCHESTRA_MCP_SERVER_NAME}${MCP_SERVER_TOOL_NAME_SEPARATOR}get_agent`, + { id: agent.name }, + { + ...mockContext, + userId: agent.authorId ?? mockContext.userId, + }, + ); + + expect(result.isError).toBe(false); + expect((result.content[0] as any).text).toContain(agent.id); + expect((result.content[0] as any).text).toContain(agent.name); + }); + + test("should fall back to name search when get_llm_proxy receives a name in id", async ({ + makeAgent, + }) => { + const llmProxy = await makeAgent({ + name: "Shared LLM Proxy", + agentType: "llm_proxy", + scope: "personal", + }); + + const result = await executeArchestraTool( + `${ARCHESTRA_MCP_SERVER_NAME}${MCP_SERVER_TOOL_NAME_SEPARATOR}get_llm_proxy`, + { id: llmProxy.name }, + { + ...mockContext, + userId: llmProxy.authorId ?? mockContext.userId, + }, + ); + + expect(result.isError).toBe(false); + expect((result.content[0] as any).text).toContain(llmProxy.id); + expect((result.content[0] as any).text).toContain(llmProxy.name); + }); + + test("should fall back to name search when get_agent receives a missing UUID id and a valid name", async ({ + makeAgent, + }) => { + const agent = await makeAgent({ + name: "Fallback Search Agent", + agentType: "agent", + scope: "personal", + }); + + const result = await executeArchestraTool( + `${ARCHESTRA_MCP_SERVER_NAME}${MCP_SERVER_TOOL_NAME_SEPARATOR}get_agent`, + { id: crypto.randomUUID(), name: agent.name }, + { + ...mockContext, + userId: agent.authorId ?? mockContext.userId, + }, + ); + + expect(result.isError).toBe(false); + expect((result.content[0] as any).text).toContain(agent.id); + expect((result.content[0] as any).text).toContain(agent.name); + }); + }); }); test("isArchestraMcpServerTool", () => { diff --git a/platform/backend/src/archestra-mcp-server.ts b/platform/backend/src/archestra-mcp-server.ts index 88975f2aeb..d4160ccfb3 100644 --- a/platform/backend/src/archestra-mcp-server.ts +++ b/platform/backend/src/archestra-mcp-server.ts @@ -86,6 +86,12 @@ function slugify(name: string): string { .replace(/^_+|_+$/g, ""); } +function isUuid(value: string): boolean { + return /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test( + value, + ); +} + // Construct fully-qualified tool names const TOOL_WHOAMI_FULL_NAME = `${ARCHESTRA_MCP_SERVER_NAME}${MCP_SERVER_TOOL_NAME_SEPARATOR}${TOOL_WHOAMI_NAME}`; const TOOL_SEARCH_PRIVATE_MCP_REGISTRY_FULL_NAME = `${ARCHESTRA_MCP_SERVER_NAME}${MCP_SERVER_TOOL_NAME_SEPARATOR}${TOOL_SEARCH_PRIVATE_MCP_REGISTRY_NAME}`; @@ -1711,15 +1717,19 @@ export async function executeArchestraTool( let record: Agent | null | undefined; - if (id) { + if (id && isUuid(id)) { record = await AgentModel.findById(id); - } else if (name) { + } + + if (!record && (name || (id && !isUuid(id)))) { + const searchName = (name ?? id) as string; + // Search by name, only matching personal agents owned by the current user const results = await AgentModel.findAllPaginated( { limit: 1, offset: 0 }, undefined, { - name, + name: searchName, agentType: expectedType, scope: "personal", authorIds: context.userId ? [context.userId] : [],