Skip to content

Commit 29f3561

Browse files
committed
refactor mcp actor and utils, use real Actor ID as standby URL
1 parent 7703eba commit 29f3561

File tree

2 files changed

+58
-56
lines changed

2 files changed

+58
-56
lines changed

src/mcp/actors.ts

Lines changed: 58 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11

22
import { ActorDefinition } from "apify-client";
3-
import { getActorDefinition, getActorStandbyURL } from "./utils.js";
3+
import { ApifyClient } from "../apify-client.js";
4+
45

56
export async function isActorMCPServer(actorID: string, apifyToken: string): Promise<boolean> {
67
const mcpPath = await getActorsMCPServerPath(actorID, apifyToken);
@@ -12,11 +13,64 @@ export async function getActorsMCPServerPath(actorID: string, apifyToken: string
1213
return (actorDefinition as any).webServerMcpPath;
1314
}
1415

15-
export async function getActorsMCPServerURL(actorID: string, _apifyToken: string): Promise<string> {
16+
export async function getActorsMCPServerURL(actorID: string, apifyToken: string): Promise<string> {
1617
// TODO: get from API instead
1718
const standbyBaseUrl = process.env.HOSTNAME === 'mcp-securitybyobscurity.apify.com' ?
1819
'securitybyobscurity.apify.actor' : 'apify.actor';
19-
const standbyUrl = getActorStandbyURL(actorID, standbyBaseUrl);
20-
const mcpPath = await getActorsMCPServerPath(actorID, _apifyToken);
20+
const standbyUrl = await getActorStandbyURL(actorID, apifyToken, standbyBaseUrl);
21+
const mcpPath = await getActorsMCPServerPath(actorID, apifyToken);
2122
return `${standbyUrl}${mcpPath}`;
2223
}
24+
25+
/**
26+
* Gets Actor ID from the Actor object.
27+
*
28+
* @param actorID
29+
*/
30+
export async function getRealActorID(actorID: string, apifyToken: string): Promise<string> {
31+
const apifyClient = new ApifyClient({ token: apifyToken });
32+
33+
const actor = apifyClient.actor(actorID);
34+
const info = await actor.get();
35+
if (!info) {
36+
throw new Error(`Actor ${actorID} not found`);
37+
}
38+
return info.id;
39+
}
40+
41+
/**
42+
* Returns standby URL for given Actor ID.
43+
*
44+
* @param actorID
45+
* @param standbyBaseUrl
46+
* @returns
47+
*/
48+
export async function getActorStandbyURL(actorID: string, apifyToken: string, standbyBaseUrl = 'apify.actor'): Promise<string> {
49+
const actorRealID = await getRealActorID(actorID, apifyToken);
50+
return `https://${actorRealID}.${standbyBaseUrl}`;
51+
}
52+
53+
export async function getActorDefinition(actorID: string, apifyToken: string): Promise<ActorDefinition> {
54+
const apifyClient = new ApifyClient({ token: apifyToken
55+
})
56+
const actor = apifyClient.actor(actorID);
57+
const info = await actor.get();
58+
if (!info) {
59+
throw new Error(`Actor ${actorID} not found`);
60+
}
61+
const latestBuildID = info.taggedBuilds?.['latest']?.buildId;
62+
if (!latestBuildID) {
63+
throw new Error(`Actor ${actorID} does not have a latest build`);
64+
}
65+
const build = apifyClient.build(latestBuildID);
66+
const buildInfo = await build.get();
67+
if (!buildInfo) {
68+
throw new Error(`Build ${latestBuildID} not found`);
69+
}
70+
const actorDefinition = buildInfo.actorDefinition;
71+
if (!actorDefinition) {
72+
throw new Error(`Build ${latestBuildID} does not have an actor definition`);
73+
}
74+
75+
return actorDefinition;
76+
}

src/mcp/utils.ts

Lines changed: 0 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,6 @@ import type { ToolWrap } from '../types.js';
88

99
import { addTool, getActorsAsTools, removeTool } from '../tools/index.js';
1010
import { Input } from "../types.js";
11-
import { APIFY_USERNAME } from "../const.js";
12-
import { ActorDefinition } from "apify-client";
13-
import { ApifyClient } from "../apify-client.js";
1411

1512
/**
1613
* Generates a unique server ID based on the provided URL.
@@ -63,52 +60,3 @@ export function parseInputParamsFromUrl(url: string): Input {
6360
const params = parse(query) as unknown as Input;
6461
return processInput(params);
6562
}
66-
67-
/**
68-
* Returns standby URL for given Actor ID.
69-
*
70-
* @param actorID
71-
* @param standbyBaseUrl
72-
* @returns
73-
*/
74-
export function getActorStandbyURL(actorID: string, standbyBaseUrl = 'apify.actor'): string {
75-
const actorOwner = actorID.split('/')[0];
76-
const actorName = actorID.split('/')[1];
77-
if (!actorOwner || !actorName) {
78-
throw new Error(`Invalid actor ID: ${actorID}`);
79-
}
80-
81-
// TODO: get from API
82-
const actorOwnerDNSFriendly = actorOwner
83-
.toLowerCase()
84-
.replace(/[^a-z0-9-]/g, '-') // only alphanumeric chars and hyphens allowed
85-
.replace(/-+/g, '-'); // replace multiple hyphens with one
86-
const prefix = actorOwner === APIFY_USERNAME ? '' : `${actorOwnerDNSFriendly}--`;
87-
88-
return `https://${prefix}${actorName}.${standbyBaseUrl}`;
89-
}
90-
91-
export async function getActorDefinition(actorID: string, apifyToken: string): Promise<ActorDefinition> {
92-
const apifyClient = new ApifyClient({ token: apifyToken
93-
})
94-
const actor = apifyClient.actor(actorID);
95-
const info = await actor.get();
96-
if (!info) {
97-
throw new Error(`Actor ${actorID} not found`);
98-
}
99-
const latestBuildID = info.taggedBuilds?.['latest']?.buildId;
100-
if (!latestBuildID) {
101-
throw new Error(`Actor ${actorID} does not have a latest build`);
102-
}
103-
const build = apifyClient.build(latestBuildID);
104-
const buildInfo = await build.get();
105-
if (!buildInfo) {
106-
throw new Error(`Build ${latestBuildID} not found`);
107-
}
108-
const actorDefinition = buildInfo.actorDefinition;
109-
if (!actorDefinition) {
110-
throw new Error(`Build ${latestBuildID} does not have an actor definition`);
111-
}
112-
113-
return actorDefinition;
114-
}

0 commit comments

Comments
 (0)