Skip to content

Commit 93e9d23

Browse files
committed
run unit only by default, add more stdio tests, fix concurrency in actor server tests
1 parent 4ec19af commit 93e9d23

File tree

5 files changed

+135
-16
lines changed

5 files changed

+135
-16
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@
6666
"build:watch": "tsc -b src -w",
6767
"type-check": "tsc --noEmit",
6868
"inspector": "npm run build && npx @modelcontextprotocol/inspector dist/stdio.js",
69-
"test": "npm run build && vitest run",
69+
"test": "npm run test:unit",
7070
"test:unit": "vitest run tests/unit",
7171
"test:integration": "npm run build && vitest run tests/integration",
7272
"clean": "tsc -b src --clean"

src/const.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,14 @@ export const defaults = {
3434
'apify/rag-web-browser',
3535
'lukaskrivka/google-maps-with-contact-details',
3636
],
37+
helperTools: [
38+
HelperTools.SEARCH_ACTORS,
39+
HelperTools.GET_ACTOR_DETAILS,
40+
],
41+
actorAddingTools: [
42+
HelperTools.ADD_ACTOR,
43+
HelperTools.REMOVE_ACTOR,
44+
],
3745
enableActorAutoLoading: false,
3846
maxMemoryMbytes: 4096,
3947
};

tests/integration/actor-server-test.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,9 @@ import { createExpressApp } from '../../src/actor/server.js';
99
import { HelperTools } from '../../src/const.js';
1010
import { ActorsMcpServer } from '../../src/mcp/server.js';
1111

12-
describe('Actors MCP Server', () => {
12+
describe('Actors MCP Server', {
13+
concurrent: false, // Run test serially to prevent port already in use
14+
}, () => {
1315
let app: Express;
1416
let server: ActorsMcpServer;
1517
let httpServer: HttpServer;

tests/integration/stdio.ts

Lines changed: 122 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
11
import { Client } from '@modelcontextprotocol/sdk/client/index.js';
22
import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js';
3-
import { afterEach, beforeEach, describe, expect, it } from 'vitest';
3+
import { describe, expect, it } from 'vitest';
4+
5+
import { defaults, HelperTools } from '../../src/const.js';
6+
import { actorNameToToolName } from '../../src/tools/utils.js';
47

58
async function createMCPClient(
69
options?: {
7-
actors: string[];
8-
enableAddingActors: boolean;
10+
actors?: string[];
11+
enableAddingActors?: boolean;
912
},
1013
): Promise<Client> {
1114
if (!process.env.APIFY_TOKEN) {
@@ -36,24 +39,129 @@ async function createMCPClient(
3639
}
3740

3841
describe('MCP STDIO', () => {
39-
let client: Client;
40-
beforeEach(async () => {
41-
client = await createMCPClient();
42+
it('list default tools', async () => {
43+
const client = await createMCPClient();
44+
const tools = await client.listTools();
45+
const names = tools.tools.map((tool) => tool.name);
46+
47+
expect(names.length).toEqual(defaults.actors.length + defaults.helperTools.length);
48+
for (const tool of defaults.helperTools) {
49+
expect(names).toContain(tool);
50+
}
51+
for (const actor of defaults.actors) {
52+
expect(names).toContain(actorNameToToolName(actor));
53+
}
54+
await client.close();
4255
});
4356

44-
afterEach(async () => {
57+
it('use only apify/python-example Actor and call it', async () => {
58+
const actorName = 'apify/python-example';
59+
const selectedToolName = actorNameToToolName(actorName);
60+
const client = await createMCPClient({
61+
actors: [actorName],
62+
enableAddingActors: false,
63+
});
64+
const tools = await client.listTools();
65+
const names = tools.tools.map((tool) => tool.name);
66+
expect(names.length).toEqual(defaults.helperTools.length + 1);
67+
for (const tool of defaults.helperTools) {
68+
expect(names).toContain(tool);
69+
}
70+
expect(names).toContain(selectedToolName);
71+
72+
const result = await client.callTool({
73+
name: selectedToolName,
74+
arguments: {
75+
first_number: 1,
76+
second_number: 2,
77+
},
78+
});
79+
80+
expect(result).toEqual({
81+
content: [{
82+
text: JSON.stringify({
83+
first_number: 1,
84+
second_number: 2,
85+
sum: 3,
86+
}),
87+
type: 'text',
88+
}],
89+
});
90+
4591
await client.close();
4692
});
4793

48-
it('list default tools', async () => {
94+
it('load Actors from parameters', async () => {
95+
const actors = ['apify/rag-web-browser', 'apify/instagram-scraper'];
96+
const client = await createMCPClient({
97+
actors,
98+
enableAddingActors: false,
99+
});
49100
const tools = await client.listTools();
50101
const names = tools.tools.map((tool) => tool.name);
102+
expect(names.length).toEqual(defaults.helperTools.length + actors.length);
103+
for (const tool of defaults.helperTools) {
104+
expect(names).toContain(tool);
105+
}
106+
for (const actor of actors) {
107+
expect(names).toContain(actorNameToToolName(actor));
108+
}
51109

52-
expect(names.length).toEqual(5);
53-
expect(names).toContain('search-actors');
54-
expect(names).toContain('get-actor-details');
55-
expect(names).toContain('apify-slash-rag-web-browser');
56-
expect(names).toContain('apify-slash-instagram-scraper');
57-
expect(names).toContain('lukaskrivka-slash-google-maps-with-contact-details');
110+
await client.close();
111+
});
112+
113+
it('load Actor dynamically and call it', async () => {
114+
const actor = 'apify/python-example';
115+
const selectedToolName = actorNameToToolName(actor);
116+
const client = await createMCPClient({
117+
enableAddingActors: true,
118+
});
119+
const tools = await client.listTools();
120+
const names = tools.tools.map((tool) => tool.name);
121+
expect(names.length).toEqual(defaults.helperTools.length + defaults.actorAddingTools.length + defaults.actors.length);
122+
for (const tool of defaults.helperTools) {
123+
expect(names).toContain(tool);
124+
}
125+
for (const tool of defaults.actorAddingTools) {
126+
expect(names).toContain(tool);
127+
}
128+
for (const actorTool of defaults.actors) {
129+
expect(names).toContain(actorNameToToolName(actorTool));
130+
}
131+
132+
// Add Actor dynamically
133+
await client.callTool({
134+
name: HelperTools.ADD_ACTOR,
135+
arguments: {
136+
actorName: actor,
137+
},
138+
});
139+
140+
// Check if tools was added
141+
const toolsAfterAdd = await client.listTools();
142+
const namesAfterAdd = toolsAfterAdd.tools.map((tool) => tool.name);
143+
expect(namesAfterAdd.length).toEqual(defaults.helperTools.length + defaults.actorAddingTools.length + defaults.actors.length + 1);
144+
expect(namesAfterAdd).toContain(selectedToolName);
145+
146+
const result = await client.callTool({
147+
name: selectedToolName,
148+
arguments: {
149+
first_number: 1,
150+
second_number: 2,
151+
},
152+
});
153+
154+
expect(result).toEqual({
155+
content: [{
156+
text: JSON.stringify({
157+
first_number: 1,
158+
second_number: 2,
159+
sum: 3,
160+
}),
161+
type: 'text',
162+
}],
163+
});
164+
165+
await client.close();
58166
});
59167
});

vitest.config.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,5 +7,6 @@ export default defineConfig({
77
globals: true,
88
environment: 'node',
99
include: ['tests/**/*.ts'],
10+
testTimeout: 60_000, // 1 minute
1011
},
1112
});

0 commit comments

Comments
 (0)