Skip to content

Commit 832e8e3

Browse files
committed
refactor: move mode-independent tools to common/, openai-only tools to openai/, and freeze all tool definitions
Move 11 mode-independent tool files to src/tools/common/ and 2 openai-only internal tools to src/tools/openai/. Wrap all ~30 tool definitions with Object.freeze() to prevent accidental mutation (e.g., Skyfire augmentation of shared objects). The existing Skyfire code path in upsertTools() already clones tools before mutation, so freezing originals is safe.
1 parent b5b3af6 commit 832e8e3

28 files changed

+155
-155
lines changed

src/index-internals.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import { ApifyClient } from './apify-client.js';
66
import { APIFY_FAVICON_URL, defaults, HelperTools, SERVER_NAME, SERVER_TITLE } from './const.js';
77
import { processParamsGetTools } from './mcp/utils.js';
88
import { getServerCard } from './server_card.js';
9-
import { addTool } from './tools/helpers.js';
9+
import { addTool } from './tools/common/helpers.js';
1010
import { defaultTools, getActorsAsTools, getUnauthEnabledToolCategories, toolCategories,
1111
toolCategoriesEnabledByDefault, unauthEnabledTools } from './tools/index.js';
1212
import { actorNameToToolName } from './tools/utils.js';

src/tools/actor.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,12 +37,12 @@ const defaultVariant = defaultCallActor as HelperTool;
3737
*
3838
* Note: The description may be overridden by tools-loader.ts at load time for openai mode.
3939
*/
40-
export const callActor: ToolEntry = {
40+
export const callActor: ToolEntry = Object.freeze({
4141
...defaultVariant,
4242
call: async (toolArgs: InternalToolArgs) => {
4343
const variant = (toolArgs.apifyMcpServer.options.uiMode === 'openai'
4444
? openaiCallActor
4545
: defaultCallActor) as HelperTool;
4646
return variant.call(toolArgs);
4747
},
48-
};
48+
});

src/tools/categories.ts

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -10,20 +10,20 @@
1010
*/
1111
import type { ToolCategory } from '../types.js';
1212
import { callActor } from './actor.js';
13-
import { getDataset, getDatasetItems, getDatasetSchema } from './dataset.js';
14-
import { getUserDatasetsList } from './dataset_collection.js';
13+
import { getDataset, getDatasetItems, getDatasetSchema } from './common/dataset.js';
14+
import { getUserDatasetsList } from './common/dataset_collection.js';
15+
import { fetchApifyDocsTool } from './common/fetch-apify-docs.js';
16+
import { getActorOutput } from './common/get-actor-output.js';
17+
import { getHtmlSkeleton } from './common/get-html-skeleton.js';
18+
import { addTool } from './common/helpers.js';
19+
import { getKeyValueStore, getKeyValueStoreKeys, getKeyValueStoreRecord } from './common/key_value_store.js';
20+
import { getUserKeyValueStoresList } from './common/key_value_store_collection.js';
21+
import { abortActorRun, getActorRun, getActorRunLog } from './common/run.js';
22+
import { getUserRunsList } from './common/run_collection.js';
23+
import { searchApifyDocsTool } from './common/search-apify-docs.js';
1524
import { fetchActorDetailsTool } from './fetch-actor-details.js';
16-
import { fetchActorDetailsInternalTool } from './fetch-actor-details-internal.js';
17-
import { fetchApifyDocsTool } from './fetch-apify-docs.js';
18-
import { getActorOutput } from './get-actor-output.js';
19-
import { getHtmlSkeleton } from './get-html-skeleton.js';
20-
import { addTool } from './helpers.js';
21-
import { getKeyValueStore, getKeyValueStoreKeys, getKeyValueStoreRecord } from './key_value_store.js';
22-
import { getUserKeyValueStoresList } from './key_value_store_collection.js';
23-
import { abortActorRun, getActorRun, getActorRunLog } from './run.js';
24-
import { getUserRunsList } from './run_collection.js';
25-
import { searchActorsInternalTool } from './search-actors-internal.js';
26-
import { searchApifyDocsTool } from './search-apify-docs.js';
25+
import { fetchActorDetailsInternalTool } from './openai/fetch-actor-details-internal.js';
26+
import { searchActorsInternalTool } from './openai/search-actors-internal.js';
2727
import { searchActors } from './store_collection.js';
2828

2929
export const toolCategories = {
Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
import { z } from 'zod';
22

3-
import { createApifyClientWithSkyfireSupport } from '../apify-client.js';
4-
import { HelperTools, TOOL_STATUS } from '../const.js';
5-
import type { InternalToolArgs, ToolEntry, ToolInputSchema } from '../types.js';
6-
import { compileSchema } from '../utils/ajv.js';
7-
import { parseCommaSeparatedList } from '../utils/generic.js';
8-
import { buildMCPResponse } from '../utils/mcp.js';
9-
import { generateSchemaFromItems } from '../utils/schema-generation.js';
10-
import { datasetItemsOutputSchema } from './structured-output-schemas.js';
3+
import { createApifyClientWithSkyfireSupport } from '../../apify-client.js';
4+
import { HelperTools, TOOL_STATUS } from '../../const.js';
5+
import type { InternalToolArgs, ToolEntry, ToolInputSchema } from '../../types.js';
6+
import { compileSchema } from '../../utils/ajv.js';
7+
import { parseCommaSeparatedList } from '../../utils/generic.js';
8+
import { buildMCPResponse } from '../../utils/mcp.js';
9+
import { generateSchemaFromItems } from '../../utils/schema-generation.js';
10+
import { datasetItemsOutputSchema } from '../structured-output-schemas.js';
1111

1212
const getDatasetArgs = z.object({
1313
datasetId: z.string()
@@ -42,7 +42,7 @@ const getDatasetItemsArgs = z.object({
4242
/**
4343
* https://docs.apify.com/api/v2/dataset-get
4444
*/
45-
export const getDataset: ToolEntry = {
45+
export const getDataset: ToolEntry = Object.freeze({
4646
type: 'internal',
4747
name: HelperTools.DATASET_GET,
4848
description: `Get metadata for a dataset (collection of structured data created by an Actor run).
@@ -83,12 +83,12 @@ USAGE EXAMPLES:
8383
}
8484
return { content: [{ type: 'text', text: `\`\`\`json\n${JSON.stringify(v)}\n\`\`\`` }] };
8585
},
86-
} as const;
86+
} as const);
8787

8888
/**
8989
* https://docs.apify.com/api/v2/dataset-items-get
9090
*/
91-
export const getDatasetItems: ToolEntry = {
91+
export const getDatasetItems: ToolEntry = Object.freeze({
9292
type: 'internal',
9393
name: HelperTools.DATASET_GET_ITEMS,
9494
description: `Retrieve dataset items with pagination, sorting, and field selection.
@@ -155,7 +155,7 @@ USAGE EXAMPLES:
155155

156156
return { content: [{ type: 'text', text: `\`\`\`json\n${JSON.stringify(v)}\n\`\`\`` }], structuredContent };
157157
},
158-
} as const;
158+
} as const);
159159

160160
const getDatasetSchemaArgs = z.object({
161161
datasetId: z.string()
@@ -175,7 +175,7 @@ const getDatasetSchemaArgs = z.object({
175175
/**
176176
* Generates a JSON schema from dataset items
177177
*/
178-
export const getDatasetSchema: ToolEntry = {
178+
export const getDatasetSchema: ToolEntry = Object.freeze({
179179
type: 'internal',
180180
name: HelperTools.DATASET_SCHEMA_GET,
181181
description: `Generate a JSON schema from a sample of dataset items.
@@ -249,4 +249,4 @@ USAGE EXAMPLES:
249249
}],
250250
};
251251
},
252-
} as const;
252+
} as const);
Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
import { z } from 'zod';
22

3-
import { ApifyClient } from '../apify-client.js';
4-
import { HelperTools } from '../const.js';
5-
import type { InternalToolArgs, ToolEntry, ToolInputSchema } from '../types.js';
6-
import { compileSchema } from '../utils/ajv.js';
3+
import { ApifyClient } from '../../apify-client.js';
4+
import { HelperTools } from '../../const.js';
5+
import type { InternalToolArgs, ToolEntry, ToolInputSchema } from '../../types.js';
6+
import { compileSchema } from '../../utils/ajv.js';
77

88
const getUserDatasetsListArgs = z.object({
99
offset: z.number()
@@ -24,7 +24,7 @@ const getUserDatasetsListArgs = z.object({
2424
/**
2525
* https://docs.apify.com/api/v2/datasets-get
2626
*/
27-
export const getUserDatasetsList: ToolEntry = {
27+
export const getUserDatasetsList: ToolEntry = Object.freeze({
2828
type: 'internal',
2929
name: HelperTools.DATASET_LIST_GET,
3030
description: `List datasets (collections of Actor run data) for the authenticated user.
@@ -59,4 +59,4 @@ USAGE EXAMPLES:
5959
});
6060
return { content: [{ type: 'text', text: `\`\`\`json\n${JSON.stringify(datasets)}\n\`\`\`` }] };
6161
},
62-
} as const;
62+
} as const);
Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,22 +2,22 @@ import { z } from 'zod';
22

33
import log from '@apify/log';
44

5-
import { ALLOWED_DOC_DOMAINS, HelperTools, TOOL_STATUS } from '../const.js';
6-
import { fetchApifyDocsCache } from '../state.js';
7-
import type { InternalToolArgs, ToolEntry, ToolInputSchema } from '../types.js';
8-
import { compileSchema } from '../utils/ajv.js';
9-
import { htmlToMarkdown } from '../utils/html-to-md.js';
10-
import { logHttpError } from '../utils/logging.js';
11-
import { buildMCPResponse } from '../utils/mcp.js';
12-
import { fetchApifyDocsToolOutputSchema } from './structured-output-schemas.js';
5+
import { ALLOWED_DOC_DOMAINS, HelperTools, TOOL_STATUS } from '../../const.js';
6+
import { fetchApifyDocsCache } from '../../state.js';
7+
import type { InternalToolArgs, ToolEntry, ToolInputSchema } from '../../types.js';
8+
import { compileSchema } from '../../utils/ajv.js';
9+
import { htmlToMarkdown } from '../../utils/html-to-md.js';
10+
import { logHttpError } from '../../utils/logging.js';
11+
import { buildMCPResponse } from '../../utils/mcp.js';
12+
import { fetchApifyDocsToolOutputSchema } from '../structured-output-schemas.js';
1313

1414
const fetchApifyDocsToolArgsSchema = z.object({
1515
url: z.string()
1616
.min(1)
1717
.describe(`URL of the Apify documentation page to fetch. This should be the full URL, including the protocol (e.g., https://docs.apify.com/).`),
1818
});
1919

20-
export const fetchApifyDocsTool: ToolEntry = {
20+
export const fetchApifyDocsTool: ToolEntry = Object.freeze({
2121
type: 'internal',
2222
name: HelperTools.DOCS_FETCH,
2323
description: `Fetch the full content of an Apify or Crawlee documentation page by its URL.
@@ -101,4 +101,4 @@ Please verify the URL is correct and accessible. You can search for available do
101101

102102
return buildMCPResponse({ texts: [`Fetched content from ${url}:\n\n${markdown}`], structuredContent: { url, content: markdown } });
103103
},
104-
} as const;
104+
} as const);
Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
import { z } from 'zod';
22

3-
import { createApifyClientWithSkyfireSupport } from '../apify-client.js';
4-
import { HelperTools, TOOL_MAX_OUTPUT_CHARS, TOOL_STATUS } from '../const.js';
5-
import type { InternalToolArgs, ToolEntry, ToolInputSchema } from '../types.js';
6-
import { compileSchema } from '../utils/ajv.js';
7-
import { getValuesByDotKeys, parseCommaSeparatedList } from '../utils/generic.js';
8-
import { buildMCPResponse } from '../utils/mcp.js';
9-
import { datasetItemsOutputSchema } from './structured-output-schemas.js';
3+
import { createApifyClientWithSkyfireSupport } from '../../apify-client.js';
4+
import { HelperTools, TOOL_MAX_OUTPUT_CHARS, TOOL_STATUS } from '../../const.js';
5+
import type { InternalToolArgs, ToolEntry, ToolInputSchema } from '../../types.js';
6+
import { compileSchema } from '../../utils/ajv.js';
7+
import { getValuesByDotKeys, parseCommaSeparatedList } from '../../utils/generic.js';
8+
import { buildMCPResponse } from '../../utils/mcp.js';
9+
import { datasetItemsOutputSchema } from '../structured-output-schemas.js';
1010

1111
/**
1212
* Zod schema for get-actor-output tool arguments
@@ -64,7 +64,7 @@ export function cleanEmptyProperties(obj: unknown): unknown {
6464
* This tool is used specifically for retrieving Actor output.
6565
* It is a simplified version of the get-dataset-items tool.
6666
*/
67-
export const getActorOutput: ToolEntry = {
67+
export const getActorOutput: ToolEntry = Object.freeze({
6868
type: 'internal',
6969
name: HelperTools.ACTOR_OUTPUT_GET,
7070
description: `Retrieve the output dataset items of a specific Actor run using its datasetId.
@@ -159,4 +159,4 @@ Note: This tool is automatically included if the Apify MCP Server is configured
159159

160160
return { content: [{ type: 'text', text: outputText }], structuredContent };
161161
},
162-
} as const;
162+
} as const);
Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
import { z } from 'zod';
22

3-
import { ApifyClient } from '../apify-client.js';
4-
import { HelperTools, RAG_WEB_BROWSER, TOOL_MAX_OUTPUT_CHARS, TOOL_STATUS } from '../const.js';
5-
import { getHtmlSkeletonCache } from '../state.js';
6-
import type { InternalToolArgs, ToolEntry, ToolInputSchema } from '../types.js';
7-
import { compileSchema } from '../utils/ajv.js';
8-
import { isValidHttpUrl } from '../utils/generic.js';
9-
import { stripHtml } from '../utils/html.js';
10-
import { buildMCPResponse } from '../utils/mcp.js';
3+
import { ApifyClient } from '../../apify-client.js';
4+
import { HelperTools, RAG_WEB_BROWSER, TOOL_MAX_OUTPUT_CHARS, TOOL_STATUS } from '../../const.js';
5+
import { getHtmlSkeletonCache } from '../../state.js';
6+
import type { InternalToolArgs, ToolEntry, ToolInputSchema } from '../../types.js';
7+
import { compileSchema } from '../../utils/ajv.js';
8+
import { isValidHttpUrl } from '../../utils/generic.js';
9+
import { stripHtml } from '../../utils/html.js';
10+
import { buildMCPResponse } from '../../utils/mcp.js';
1111

1212
type ScrapedPageItem = {
1313
crawl: {
@@ -35,7 +35,7 @@ const getHtmlSkeletonArgs = z.object({
3535
.describe('Chunk number to retrieve when getting the content. The content is split into chunks to prevent exceeding the maximum tool output length.'),
3636
});
3737

38-
export const getHtmlSkeleton: ToolEntry = {
38+
export const getHtmlSkeleton: ToolEntry = Object.freeze({
3939
type: 'internal',
4040
name: HelperTools.GET_HTML_SKELETON,
4141
description: `Retrieve the HTML skeleton (clean structure) of a webpage by stripping scripts, styles, and non-essential attributes.
@@ -123,4 +123,4 @@ USAGE EXAMPLES:
123123

124124
return buildMCPResponse({ texts: [chunkContent + chunkInfo] });
125125
},
126-
} as const;
126+
} as const);
Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,16 @@
11
import { z } from 'zod';
22

3-
import { ApifyClient } from '../apify-client.js';
4-
import { HelperTools } from '../const.js';
5-
import type { InternalToolArgs, ToolEntry, ToolInputSchema } from '../types.js';
6-
import { compileSchema } from '../utils/ajv.js';
3+
import { ApifyClient } from '../../apify-client.js';
4+
import { HelperTools } from '../../const.js';
5+
import type { InternalToolArgs, ToolEntry, ToolInputSchema } from '../../types.js';
6+
import { compileSchema } from '../../utils/ajv.js';
77

88
export const addToolArgsSchema = z.object({
99
actor: z.string()
1010
.min(1)
1111
.describe(`Actor ID or full name in the format "username/name", e.g., "apify/rag-web-browser".`),
1212
});
13-
export const addTool: ToolEntry = {
13+
export const addTool: ToolEntry = Object.freeze({
1414
type: 'internal',
1515
name: HelperTools.ACTOR_ADD,
1616
description: `Add an Actor or MCP server to the Apify MCP Server as an available tool.
@@ -75,4 +75,4 @@ USAGE EXAMPLES:
7575
}],
7676
};
7777
},
78-
} as const;
78+
} as const);
Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
import { z } from 'zod';
22

3-
import { createApifyClientWithSkyfireSupport } from '../apify-client.js';
4-
import { HelperTools } from '../const.js';
5-
import type { InternalToolArgs, ToolEntry, ToolInputSchema } from '../types.js';
6-
import { compileSchema } from '../utils/ajv.js';
3+
import { createApifyClientWithSkyfireSupport } from '../../apify-client.js';
4+
import { HelperTools } from '../../const.js';
5+
import type { InternalToolArgs, ToolEntry, ToolInputSchema } from '../../types.js';
6+
import { compileSchema } from '../../utils/ajv.js';
77

88
const getKeyValueStoreArgs = z.object({
99
storeId: z.string()
@@ -14,7 +14,7 @@ const getKeyValueStoreArgs = z.object({
1414
/**
1515
* https://docs.apify.com/api/v2/key-value-store-get
1616
*/
17-
export const getKeyValueStore: ToolEntry = {
17+
export const getKeyValueStore: ToolEntry = Object.freeze({
1818
type: 'internal',
1919
name: HelperTools.KEY_VALUE_STORE_GET,
2020
description: `Get details about a key-value store by ID or username~store-name.
@@ -46,7 +46,7 @@ USAGE EXAMPLES:
4646
const store = await client.keyValueStore(parsed.storeId).get();
4747
return { content: [{ type: 'text', text: `\`\`\`json\n${JSON.stringify(store)}\n\`\`\`` }] };
4848
},
49-
} as const;
49+
} as const);
5050

5151
const getKeyValueStoreKeysArgs = z.object({
5252
storeId: z.string()
@@ -64,7 +64,7 @@ const getKeyValueStoreKeysArgs = z.object({
6464
/**
6565
* https://docs.apify.com/api/v2/key-value-store-keys-get
6666
*/
67-
export const getKeyValueStoreKeys: ToolEntry = {
67+
export const getKeyValueStoreKeys: ToolEntry = Object.freeze({
6868
type: 'internal',
6969
name: HelperTools.KEY_VALUE_STORE_KEYS_GET,
7070
description: `List keys in a key-value store with optional pagination.
@@ -100,7 +100,7 @@ USAGE EXAMPLES:
100100
});
101101
return { content: [{ type: 'text', text: `\`\`\`json\n${JSON.stringify(keys)}\n\`\`\`` }] };
102102
},
103-
} as const;
103+
} as const);
104104

105105
const getKeyValueStoreRecordArgs = z.object({
106106
storeId: z.string()
@@ -114,7 +114,7 @@ const getKeyValueStoreRecordArgs = z.object({
114114
/**
115115
* https://docs.apify.com/api/v2/key-value-store-record-get
116116
*/
117-
export const getKeyValueStoreRecord: ToolEntry = {
117+
export const getKeyValueStoreRecord: ToolEntry = Object.freeze({
118118
type: 'internal',
119119
name: HelperTools.KEY_VALUE_STORE_RECORD_GET,
120120
description: `Get a value stored in a key-value store under a specific key.
@@ -146,4 +146,4 @@ USAGE EXAMPLES:
146146
const record = await client.keyValueStore(parsed.storeId).getRecord(parsed.recordKey);
147147
return { content: [{ type: 'text', text: `\`\`\`json\n${JSON.stringify(record)}\n\`\`\`` }] };
148148
},
149-
} as const;
149+
} as const);

0 commit comments

Comments
 (0)