@inspectr/mcplab-mcp-server 1.8.3 → 1.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/browser-provider-library.test.d.ts +2 -0
- package/dist/browser-provider-library.test.d.ts.map +1 -0
- package/dist/browser-provider-library.test.js +129 -0
- package/dist/browser-provider-library.test.js.map +1 -0
- package/dist/http-handler.d.ts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/mcp-run-calculations.d.ts +35 -42
- package/dist/mcp-run-calculations.d.ts.map +1 -1
- package/dist/mcp-run-calculations.js +16 -7
- package/dist/mcp-run-calculations.js.map +1 -1
- package/dist/prompts.d.ts +1 -1
- package/dist/runtime.contracts.test.d.ts +1 -1
- package/dist/runtime.contracts.test.js +37 -0
- package/dist/runtime.contracts.test.js.map +1 -1
- package/dist/runtime.d.ts +15 -21
- package/dist/runtime.d.ts.map +1 -1
- package/dist/runtime.decision.test.d.ts +2 -0
- package/dist/runtime.decision.test.d.ts.map +1 -0
- package/dist/runtime.decision.test.js +141 -0
- package/dist/runtime.decision.test.js.map +1 -0
- package/dist/runtime.js +149 -36
- package/dist/runtime.js.map +1 -1
- package/dist/server-factory.d.ts +1 -1
- package/dist/skills/mcplab-assistant/SKILL.md +17 -2
- package/dist/skills/mcplab-assistant/references/browser-agents.md +122 -0
- package/dist/skills.d.ts +21 -29
- package/dist/skills.js.map +1 -1
- package/dist/skills.test.d.ts +1 -1
- package/dist/tools.d.ts +1 -1
- package/package.json +5 -5
package/dist/runtime.d.ts
CHANGED
|
@@ -2,34 +2,28 @@ import { type IncomingMessage, type ServerResponse } from 'node:http';
|
|
|
2
2
|
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
3
3
|
import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
|
|
4
4
|
export type SessionRuntime = {
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
transport: StreamableHTTPServerTransport;
|
|
6
|
+
server: McpServer;
|
|
7
7
|
};
|
|
8
8
|
export interface McplabMcpServerOptions {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
9
|
+
host: string;
|
|
10
|
+
port: number;
|
|
11
|
+
path: string;
|
|
12
|
+
logger?: Pick<Console, 'log' | 'error'>;
|
|
13
13
|
}
|
|
14
14
|
export interface McplabMcpServerRuntime {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
15
|
+
host: string;
|
|
16
|
+
port: number;
|
|
17
|
+
path: string;
|
|
18
|
+
close(): Promise<void>;
|
|
19
19
|
}
|
|
20
|
-
export declare function startMcplabMcpServer(
|
|
21
|
-
options: McplabMcpServerOptions
|
|
22
|
-
): Promise<McplabMcpServerRuntime>;
|
|
20
|
+
export declare function startMcplabMcpServer(options: McplabMcpServerOptions): Promise<McplabMcpServerRuntime>;
|
|
23
21
|
export declare function defaultMcplabMcpServerOptionsFromEnv(): McplabMcpServerOptions;
|
|
24
22
|
export declare function createConfiguredServer(): McpServer;
|
|
25
23
|
export declare function registerTools(server: McpServer): void;
|
|
26
24
|
export declare function registerPrompts(server: McpServer): void;
|
|
27
|
-
export declare function handleMcplabMcpHttpRequest(
|
|
28
|
-
req: IncomingMessage,
|
|
29
|
-
res: ServerResponse,
|
|
30
|
-
sessions: Map<string, SessionRuntime>,
|
|
31
|
-
options?: {
|
|
25
|
+
export declare function handleMcplabMcpHttpRequest(req: IncomingMessage, res: ServerResponse, sessions: Map<string, SessionRuntime>, options?: {
|
|
32
26
|
path?: string;
|
|
33
|
-
|
|
34
|
-
): Promise<void>;
|
|
35
|
-
//# sourceMappingURL=runtime.d.ts.map
|
|
27
|
+
logger?: Pick<Console, 'log' | 'error'>;
|
|
28
|
+
}): Promise<void>;
|
|
29
|
+
//# sourceMappingURL=runtime.d.ts.map
|
package/dist/runtime.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../src/runtime.ts"],"names":[],"mappings":"AAUA,OAAO,EAAgB,KAAK,eAAe,EAAE,KAAK,cAAc,EAAE,MAAM,WAAW,CAAC;AAEpF,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACpE,OAAO,EAAE,6BAA6B,EAAE,MAAM,oDAAoD,CAAC;
|
|
1
|
+
{"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../src/runtime.ts"],"names":[],"mappings":"AAUA,OAAO,EAAgB,KAAK,eAAe,EAAE,KAAK,cAAc,EAAE,MAAM,WAAW,CAAC;AAEpF,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACpE,OAAO,EAAE,6BAA6B,EAAE,MAAM,oDAAoD,CAAC;AAkvBnG,MAAM,MAAM,cAAc,GAAG;IAC3B,SAAS,EAAE,6BAA6B,CAAC;IACzC,MAAM,EAAE,SAAS,CAAC;CACnB,CAAC;AAEF,MAAM,WAAW,sBAAsB;IACrC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,IAAI,CAAC,OAAO,EAAE,KAAK,GAAG,OAAO,CAAC,CAAC;CACzC;AAED,MAAM,WAAW,sBAAsB;IACrC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACxB;AAED,wBAAsB,oBAAoB,CACxC,OAAO,EAAE,sBAAsB,GAC9B,OAAO,CAAC,sBAAsB,CAAC,CAoDjC;AAED,wBAAgB,oCAAoC,IAAI,sBAAsB,CAM7E;AAED,wBAAgB,sBAAsB,IAAI,SAAS,CAclD;AA8CD,wBAAgB,aAAa,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CA0zErD;AAED,wBAAgB,eAAe,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CA0EvD;AAqvCD,wBAAsB,0BAA0B,CAC9C,GAAG,EAAE,eAAe,EACpB,GAAG,EAAE,cAAc,EACnB,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,cAAc,CAAC,EACrC,OAAO,CAAC,EAAE;IAAE,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,IAAI,CAAC,OAAO,EAAE,KAAK,GAAG,OAAO,CAAC,CAAA;CAAE,GACnE,OAAO,CAAC,IAAI,CAAC,CAQf"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runtime.decision.test.d.ts","sourceRoot":"","sources":["../src/runtime.decision.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
|
2
|
+
import { aggregateResults } from '@inspectr/mcplab-core';
|
|
3
|
+
const mocks = vi.hoisted(() => ({
|
|
4
|
+
runAll: vi.fn(),
|
|
5
|
+
loadConfig: vi.fn(),
|
|
6
|
+
loadWorkspaceSettingsOverrides: vi.fn(),
|
|
7
|
+
readLibraryAgentsAndServers: vi.fn()
|
|
8
|
+
}));
|
|
9
|
+
vi.mock('@inspectr/mcplab-core', async (importOriginal) => ({
|
|
10
|
+
...(await importOriginal()),
|
|
11
|
+
...mocks
|
|
12
|
+
}));
|
|
13
|
+
import { registerTools } from './runtime.js';
|
|
14
|
+
describe('immediate MCP decision evaluation', () => {
|
|
15
|
+
const agent = { provider: 'openai', model: 'gpt-5-mini' };
|
|
16
|
+
const decision = {
|
|
17
|
+
provider: 'ollama',
|
|
18
|
+
model: 'clef-flash',
|
|
19
|
+
passThreshold: 0.9,
|
|
20
|
+
failThreshold: 0.1,
|
|
21
|
+
baseUrl: 'http://localhost:11434'
|
|
22
|
+
};
|
|
23
|
+
let runTool;
|
|
24
|
+
beforeEach(() => {
|
|
25
|
+
vi.clearAllMocks();
|
|
26
|
+
mocks.loadConfig.mockReturnValue({
|
|
27
|
+
config: {
|
|
28
|
+
name: 'test',
|
|
29
|
+
servers: {},
|
|
30
|
+
agents: {},
|
|
31
|
+
scenarios: [
|
|
32
|
+
{
|
|
33
|
+
id: 'semantic',
|
|
34
|
+
servers: [],
|
|
35
|
+
prompt: 'Answer',
|
|
36
|
+
eval: { agent_assertions: [{ label: 'Check', prompt: 'Valid?' }] }
|
|
37
|
+
}
|
|
38
|
+
]
|
|
39
|
+
},
|
|
40
|
+
hash: 'test'
|
|
41
|
+
});
|
|
42
|
+
mocks.readLibraryAgentsAndServers.mockReturnValue({ agents: { judge: agent }, servers: {} });
|
|
43
|
+
mocks.runAll.mockResolvedValue({
|
|
44
|
+
runDir: '/tmp/mcplab-mcp-test',
|
|
45
|
+
results: aggregateResults({
|
|
46
|
+
runId: 'test',
|
|
47
|
+
timestamp: '2026-10-13T00:00:00Z',
|
|
48
|
+
configHash: 'test',
|
|
49
|
+
cliVersion: 'test',
|
|
50
|
+
scenarioRuns: []
|
|
51
|
+
})
|
|
52
|
+
});
|
|
53
|
+
registerTools({
|
|
54
|
+
registerTool: (name, _config, cb) => {
|
|
55
|
+
if (name === 'mcplab_run_eval')
|
|
56
|
+
runTool = cb;
|
|
57
|
+
return { name };
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
});
|
|
61
|
+
it.each(['deleted', 'browser'])('runs deterministic checks despite a %s workspace judge', async (name) => {
|
|
62
|
+
mocks.loadConfig.mockReturnValue({
|
|
63
|
+
config: {
|
|
64
|
+
servers: {},
|
|
65
|
+
agents: {},
|
|
66
|
+
scenarios: [
|
|
67
|
+
{
|
|
68
|
+
id: 'deterministic',
|
|
69
|
+
servers: [],
|
|
70
|
+
prompt: 'Answer',
|
|
71
|
+
eval: { response_assertions: [{ type: 'contains', value: 'done' }] }
|
|
72
|
+
}
|
|
73
|
+
]
|
|
74
|
+
}
|
|
75
|
+
});
|
|
76
|
+
mocks.loadWorkspaceSettingsOverrides.mockReturnValue({
|
|
77
|
+
evaluation_judge_agent_name: name,
|
|
78
|
+
evaluation_decision: decision
|
|
79
|
+
});
|
|
80
|
+
mocks.readLibraryAgentsAndServers.mockReturnValue({
|
|
81
|
+
agents: {
|
|
82
|
+
main: agent,
|
|
83
|
+
browser: { type: 'browser', provider: 'polo', url: 'https://polo.example' }
|
|
84
|
+
},
|
|
85
|
+
servers: {}
|
|
86
|
+
});
|
|
87
|
+
const result = await runTool({ config_path: '/tmp/eval.yaml' });
|
|
88
|
+
expect(result.isError).not.toBe(true);
|
|
89
|
+
const options = mocks.runAll.mock.calls[0][1];
|
|
90
|
+
expect(options.evaluationJudge).toBeUndefined();
|
|
91
|
+
expect(options.evaluationDecision).toBeUndefined();
|
|
92
|
+
});
|
|
93
|
+
it('uses shared workspace decision configuration and LLM judge backup', async () => {
|
|
94
|
+
mocks.loadWorkspaceSettingsOverrides.mockReturnValue({
|
|
95
|
+
evaluation_judge_agent_name: 'judge',
|
|
96
|
+
evaluation_decision: decision
|
|
97
|
+
});
|
|
98
|
+
const result = await runTool({ config_path: '/tmp/eval.yaml' });
|
|
99
|
+
expect(result.isError).not.toBe(true);
|
|
100
|
+
expect(mocks.runAll).toHaveBeenCalledWith(expect.anything(), expect.objectContaining({
|
|
101
|
+
evaluationDecision: decision,
|
|
102
|
+
evaluationJudge: { name: 'judge', agent }
|
|
103
|
+
}));
|
|
104
|
+
expect(mocks.loadWorkspaceSettingsOverrides).toHaveBeenCalledWith(mocks.readLibraryAgentsAndServers.mock.calls[0][0]);
|
|
105
|
+
});
|
|
106
|
+
it('preserves legacy judge-only workspaces', async () => {
|
|
107
|
+
mocks.loadWorkspaceSettingsOverrides.mockReturnValue({ evaluation_judge_agent_name: 'judge' });
|
|
108
|
+
await runTool({ config_path: '/tmp/eval.yaml' });
|
|
109
|
+
expect(mocks.runAll).toHaveBeenCalledWith(expect.anything(), expect.objectContaining({
|
|
110
|
+
evaluationDecision: undefined,
|
|
111
|
+
evaluationJudge: { name: 'judge', agent }
|
|
112
|
+
}));
|
|
113
|
+
});
|
|
114
|
+
it('passes malformed persisted decision settings through for shared fallback handling', async () => {
|
|
115
|
+
const malformed = { provider: 'missing', passThreshold: 2 };
|
|
116
|
+
mocks.loadWorkspaceSettingsOverrides.mockReturnValue({
|
|
117
|
+
evaluation_judge_agent_name: 'judge',
|
|
118
|
+
evaluation_decision: malformed
|
|
119
|
+
});
|
|
120
|
+
await runTool({ config_path: '/tmp/eval.yaml' });
|
|
121
|
+
expect(mocks.runAll).toHaveBeenCalledWith(expect.anything(), expect.objectContaining({
|
|
122
|
+
evaluationDecision: malformed,
|
|
123
|
+
evaluationJudge: { name: 'judge', agent }
|
|
124
|
+
}));
|
|
125
|
+
});
|
|
126
|
+
it('rejects a browser agent selected as judge before starting execution', async () => {
|
|
127
|
+
mocks.loadWorkspaceSettingsOverrides.mockReturnValue({
|
|
128
|
+
evaluation_judge_agent_name: 'judge',
|
|
129
|
+
evaluation_decision: decision
|
|
130
|
+
});
|
|
131
|
+
mocks.readLibraryAgentsAndServers.mockReturnValue({
|
|
132
|
+
agents: { judge: { type: 'browser', provider: 'polo', url: 'https://polo.example' } },
|
|
133
|
+
servers: {}
|
|
134
|
+
});
|
|
135
|
+
const result = await runTool({ config_path: '/tmp/eval.yaml' });
|
|
136
|
+
expect(result.isError).toBe(true);
|
|
137
|
+
expect(result.content[0].text).toContain('LLM');
|
|
138
|
+
expect(mocks.runAll).not.toHaveBeenCalled();
|
|
139
|
+
});
|
|
140
|
+
});
|
|
141
|
+
//# sourceMappingURL=runtime.decision.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runtime.decision.test.js","sourceRoot":"","sources":["../src/runtime.decision.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAC9D,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAEzD,MAAM,KAAK,GAAG,EAAE,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC;IAC9B,MAAM,EAAE,EAAE,CAAC,EAAE,EAAE;IACf,UAAU,EAAE,EAAE,CAAC,EAAE,EAAE;IACnB,8BAA8B,EAAE,EAAE,CAAC,EAAE,EAAE;IACvC,2BAA2B,EAAE,EAAE,CAAC,EAAE,EAAE;CACrC,CAAC,CAAC,CAAC;AACJ,EAAE,CAAC,IAAI,CAAC,uBAAuB,EAAE,KAAK,EAAE,cAAc,EAAE,EAAE,CAAC,CAAC;IAC1D,GAAG,CAAC,MAAM,cAAc,EAA0C,CAAC;IACnE,GAAG,KAAK;CACT,CAAC,CAAC,CAAC;AACJ,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAE7C,QAAQ,CAAC,mCAAmC,EAAE,GAAG,EAAE;IACjD,MAAM,KAAK,GAAG,EAAE,QAAQ,EAAE,QAAiB,EAAE,KAAK,EAAE,YAAY,EAAE,CAAC;IACnE,MAAM,QAAQ,GAAG;QACf,QAAQ,EAAE,QAAiB;QAC3B,KAAK,EAAE,YAAY;QACnB,aAAa,EAAE,GAAG;QAClB,aAAa,EAAE,GAAG;QAClB,OAAO,EAAE,wBAAwB;KAClC,CAAC;IACF,IAAI,OAAwD,CAAC;IAE7D,UAAU,CAAC,GAAG,EAAE;QACd,EAAE,CAAC,aAAa,EAAE,CAAC;QACnB,KAAK,CAAC,UAAU,CAAC,eAAe,CAAC;YAC/B,MAAM,EAAE;gBACN,IAAI,EAAE,MAAM;gBACZ,OAAO,EAAE,EAAE;gBACX,MAAM,EAAE,EAAE;gBACV,SAAS,EAAE;oBACT;wBACE,EAAE,EAAE,UAAU;wBACd,OAAO,EAAE,EAAE;wBACX,MAAM,EAAE,QAAQ;wBAChB,IAAI,EAAE,EAAE,gBAAgB,EAAE,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,EAAE;qBACnE;iBACF;aACF;YACD,IAAI,EAAE,MAAM;SACb,CAAC,CAAC;QACH,KAAK,CAAC,2BAA2B,CAAC,eAAe,CAAC,EAAE,MAAM,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE,CAAC,CAAC;QAC7F,KAAK,CAAC,MAAM,CAAC,iBAAiB,CAAC;YAC7B,MAAM,EAAE,sBAAsB;YAC9B,OAAO,EAAE,gBAAgB,CAAC;gBACxB,KAAK,EAAE,MAAM;gBACb,SAAS,EAAE,sBAAsB;gBACjC,UAAU,EAAE,MAAM;gBAClB,UAAU,EAAE,MAAM;gBAClB,YAAY,EAAE,EAAE;aACjB,CAAC;SACH,CAAC,CAAC;QACH,aAAa,CAAC;YACZ,YAAY,EAAE,CAAC,IAAY,EAAE,OAAgB,EAAE,EAAkB,EAAE,EAAE;gBACnE,IAAI,IAAI,KAAK,iBAAiB;oBAAE,OAAO,GAAG,EAAE,CAAC;gBAC7C,OAAO,EAAE,IAAI,EAAE,CAAC;YAClB,CAAC;SACK,CAAC,CAAC;IACZ,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,IAAI,CAAC,CAAC,SAAS,EAAE,SAAS,CAAC,CAAC,CAC7B,wDAAwD,EACxD,KAAK,EAAE,IAAI,EAAE,EAAE;QACb,KAAK,CAAC,UAAU,CAAC,eAAe,CAAC;YAC/B,MAAM,EAAE;gBACN,OAAO,EAAE,EAAE;gBACX,MAAM,EAAE,EAAE;gBACV,SAAS,EAAE;oBACT;wBACE,EAAE,EAAE,eAAe;wBACnB,OAAO,EAAE,EAAE;wBACX,MAAM,EAAE,QAAQ;wBAChB,IAAI,EAAE,EAAE,mBAAmB,EAAE,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,EAAE;qBACrE;iBACF;aACF;SACF,CAAC,CAAC;QACH,KAAK,CAAC,8BAA8B,CAAC,eAAe,CAAC;YACnD,2BAA2B,EAAE,IAAI;YACjC,mBAAmB,EAAE,QAAQ;SAC9B,CAAC,CAAC;QACH,KAAK,CAAC,2BAA2B,CAAC,eAAe,CAAC;YAChD,MAAM,EAAE;gBACN,IAAI,EAAE,KAAK;gBACX,OAAO,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,EAAE,sBAAsB,EAAE;aAC5E;YACD,OAAO,EAAE,EAAE;SACZ,CAAC,CAAC;QACH,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,EAAE,WAAW,EAAE,gBAAgB,EAAE,CAAC,CAAC;QAChE,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACtC,MAAM,OAAO,GAAG,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QAC9C,MAAM,CAAC,OAAO,CAAC,eAAe,CAAC,CAAC,aAAa,EAAE,CAAC;QAChD,MAAM,CAAC,OAAO,CAAC,kBAAkB,CAAC,CAAC,aAAa,EAAE,CAAC;IACrD,CAAC,CACF,CAAC;IAEF,EAAE,CAAC,mEAAmE,EAAE,KAAK,IAAI,EAAE;QACjF,KAAK,CAAC,8BAA8B,CAAC,eAAe,CAAC;YACnD,2BAA2B,EAAE,OAAO;YACpC,mBAAmB,EAAE,QAAQ;SAC9B,CAAC,CAAC;QACH,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,EAAE,WAAW,EAAE,gBAAgB,EAAE,CAAC,CAAC;QAChE,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACtC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,oBAAoB,CACvC,MAAM,CAAC,QAAQ,EAAE,EACjB,MAAM,CAAC,gBAAgB,CAAC;YACtB,kBAAkB,EAAE,QAAQ;YAC5B,eAAe,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE;SAC1C,CAAC,CACH,CAAC;QACF,MAAM,CAAC,KAAK,CAAC,8BAA8B,CAAC,CAAC,oBAAoB,CAC/D,KAAK,CAAC,2BAA2B,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CACnD,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,wCAAwC,EAAE,KAAK,IAAI,EAAE;QACtD,KAAK,CAAC,8BAA8B,CAAC,eAAe,CAAC,EAAE,2BAA2B,EAAE,OAAO,EAAE,CAAC,CAAC;QAC/F,MAAM,OAAO,CAAC,EAAE,WAAW,EAAE,gBAAgB,EAAE,CAAC,CAAC;QACjD,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,oBAAoB,CACvC,MAAM,CAAC,QAAQ,EAAE,EACjB,MAAM,CAAC,gBAAgB,CAAC;YACtB,kBAAkB,EAAE,SAAS;YAC7B,eAAe,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE;SAC1C,CAAC,CACH,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,mFAAmF,EAAE,KAAK,IAAI,EAAE;QACjG,MAAM,SAAS,GAAG,EAAE,QAAQ,EAAE,SAAS,EAAE,aAAa,EAAE,CAAC,EAAE,CAAC;QAC5D,KAAK,CAAC,8BAA8B,CAAC,eAAe,CAAC;YACnD,2BAA2B,EAAE,OAAO;YACpC,mBAAmB,EAAE,SAAS;SAC/B,CAAC,CAAC;QACH,MAAM,OAAO,CAAC,EAAE,WAAW,EAAE,gBAAgB,EAAE,CAAC,CAAC;QACjD,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,oBAAoB,CACvC,MAAM,CAAC,QAAQ,EAAE,EACjB,MAAM,CAAC,gBAAgB,CAAC;YACtB,kBAAkB,EAAE,SAAS;YAC7B,eAAe,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE;SAC1C,CAAC,CACH,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,qEAAqE,EAAE,KAAK,IAAI,EAAE;QACnF,KAAK,CAAC,8BAA8B,CAAC,eAAe,CAAC;YACnD,2BAA2B,EAAE,OAAO;YACpC,mBAAmB,EAAE,QAAQ;SAC9B,CAAC,CAAC;QACH,KAAK,CAAC,2BAA2B,CAAC,eAAe,CAAC;YAChD,MAAM,EAAE,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,EAAE,sBAAsB,EAAE,EAAE;YACrF,OAAO,EAAE,EAAE;SACZ,CAAC,CAAC;QACH,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,EAAE,WAAW,EAAE,gBAAgB,EAAE,CAAC,CAAC;QAChE,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAClC,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;QAChD,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,gBAAgB,EAAE,CAAC;IAC9C,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
package/dist/runtime.js
CHANGED
|
@@ -5,7 +5,7 @@ import { basename, dirname, extname, resolve, join, sep, relative } from 'node:p
|
|
|
5
5
|
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
6
6
|
import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
|
|
7
7
|
import { isInitializeRequest } from '@modelcontextprotocol/sdk/types.js';
|
|
8
|
-
import { loadConfig, hashConfig, runAll, selectScenarios, loadOrBuildSearchIndex, indexNeedsRefresh, getResultsIndexPaths, resolveRunArtifactPath, searchDocs, getContext, applyRuntimeServerOverrides, readLibraryAgentsAndServers, resolveScenarioLibraryDir, createEvaluationConfigFile, updateEvaluationConfigFile, readEvaluationConfigFile, createTestCaseFile, readTestCaseFile, updateTestCaseFile, buildScenarioEntry } from '@inspectr/mcplab-core';
|
|
8
|
+
import { loadConfig, hashConfig, runAll, loadWorkspaceSettingsOverrides, toWorkspaceEvaluationSettings, resolveWorkspaceEvaluation, selectScenarios, loadOrBuildSearchIndex, indexNeedsRefresh, getResultsIndexPaths, resolveRunArtifactPath, searchDocs, getContext, applyRuntimeServerOverrides, readLibraryAgentsAndServers, readBrowserProviderProfiles, serializeBrowserProviderProfile, resolveScenarioLibraryDir, createEvaluationConfigFile, updateEvaluationConfigFile, readEvaluationConfigFile, createTestCaseFile, readTestCaseFile, updateTestCaseFile, buildScenarioEntry, DEFAULT_BROWSER_PROVIDER_PROFILES, mergeBrowserProviderProfiles } from '@inspectr/mcplab-core';
|
|
9
9
|
import { renderReport } from '@inspectr/mcplab-reporting';
|
|
10
10
|
import { parse as parseYaml, stringify as stringifyYaml } from 'yaml';
|
|
11
11
|
import { z } from 'zod';
|
|
@@ -25,7 +25,7 @@ const GenericObjectSchema = z.object({}).passthrough();
|
|
|
25
25
|
const ResultsSummarySchema = z.object({
|
|
26
26
|
total_scenarios: z.number().int().nonnegative(),
|
|
27
27
|
total_runs: z.number().int().nonnegative(),
|
|
28
|
-
pass_rate: z.number(),
|
|
28
|
+
pass_rate: z.number().nullable(),
|
|
29
29
|
avg_tool_calls_per_run: z.number(),
|
|
30
30
|
avg_tool_latency_ms: z.number().nullable()
|
|
31
31
|
});
|
|
@@ -44,7 +44,8 @@ const MetricSummarySchema = z.object({
|
|
|
44
44
|
total_runs: z.number().int().nonnegative(),
|
|
45
45
|
passed_runs: z.number().int().nonnegative(),
|
|
46
46
|
failed_runs: z.number().int().nonnegative(),
|
|
47
|
-
|
|
47
|
+
stopped_runs: z.number().int().nonnegative().optional(),
|
|
48
|
+
pass_rate: z.number().nullable(),
|
|
48
49
|
avg_tool_calls_per_run: z.number(),
|
|
49
50
|
avg_tool_latency_ms: z.number().nullable()
|
|
50
51
|
});
|
|
@@ -66,7 +67,7 @@ const CompareRowSchema = z.object({
|
|
|
66
67
|
key: z.string(),
|
|
67
68
|
scenario_id: z.string(),
|
|
68
69
|
agent: z.string(),
|
|
69
|
-
classification: z.enum(['regressed', 'improved', 'unchanged', 'new', 'missing']),
|
|
70
|
+
classification: z.enum(['regressed', 'improved', 'unchanged', 'new', 'missing', 'not_evaluated']),
|
|
70
71
|
left: MetricSummarySchema.nullable(),
|
|
71
72
|
right: MetricSummarySchema.nullable(),
|
|
72
73
|
deltas: z.object({
|
|
@@ -90,12 +91,19 @@ const LibraryScenarioEntrySchema = z.object({
|
|
|
90
91
|
yaml: z.string().optional()
|
|
91
92
|
});
|
|
92
93
|
const AgentEntrySchema = z.object({
|
|
93
|
-
provider: z.enum(['openai', 'anthropic', 'azure_openai']),
|
|
94
|
+
provider: z.enum(['openai', 'anthropic', 'azure_openai', 'azure_foundry']),
|
|
94
95
|
model: z.string(),
|
|
95
96
|
temperature: z.number().optional(),
|
|
96
97
|
max_tokens: z.number().int().positive().optional(),
|
|
97
98
|
system: z.string().optional()
|
|
98
99
|
});
|
|
100
|
+
const BrowserAgentEntrySchema = z.object({
|
|
101
|
+
type: z.literal('browser'),
|
|
102
|
+
name: z.string().optional(),
|
|
103
|
+
provider: z.string(),
|
|
104
|
+
url: z.string(),
|
|
105
|
+
new_conversation_between_scenarios: z.boolean().optional()
|
|
106
|
+
});
|
|
99
107
|
const LibraryServerEntryContentSchema = z
|
|
100
108
|
.object({
|
|
101
109
|
transport: z.string().optional(),
|
|
@@ -112,12 +120,17 @@ const LibraryServerEntrySchema = z.object({
|
|
|
112
120
|
});
|
|
113
121
|
const LibraryAgentEntrySchema = z.object({
|
|
114
122
|
id: z.string(),
|
|
115
|
-
entry: AgentEntrySchema.optional()
|
|
123
|
+
entry: z.union([AgentEntrySchema, BrowserAgentEntrySchema]).optional()
|
|
124
|
+
});
|
|
125
|
+
const LibraryBrowserProviderEntrySchema = z.object({
|
|
126
|
+
id: z.string(),
|
|
127
|
+
entry: GenericObjectSchema.optional()
|
|
116
128
|
});
|
|
117
129
|
const LibraryEntrySchema = z.object({
|
|
118
130
|
bundleRoot: z.string(),
|
|
119
131
|
servers: z.array(LibraryServerEntrySchema),
|
|
120
132
|
agents: z.array(LibraryAgentEntrySchema),
|
|
133
|
+
browser_providers: z.array(LibraryBrowserProviderEntrySchema),
|
|
121
134
|
test_cases: z.array(LibraryScenarioEntrySchema),
|
|
122
135
|
scenarios: z.array(LibraryScenarioEntrySchema)
|
|
123
136
|
});
|
|
@@ -615,7 +628,7 @@ export async function startMcplabMcpServer(options) {
|
|
|
615
628
|
const sessions = new Map();
|
|
616
629
|
const httpServer = createServer(async (req, res) => {
|
|
617
630
|
try {
|
|
618
|
-
await handleHttpRequest(req, res, sessions, options.path);
|
|
631
|
+
await handleHttpRequest(req, res, sessions, options.path, logger);
|
|
619
632
|
}
|
|
620
633
|
catch (error) {
|
|
621
634
|
logger.error('[mcplab-mcp] request error:', error);
|
|
@@ -1081,7 +1094,7 @@ export function registerTools(server) {
|
|
|
1081
1094
|
outputSchema: LibraryEntrySchema,
|
|
1082
1095
|
inputSchema: {
|
|
1083
1096
|
kind: z
|
|
1084
|
-
.enum(['all', 'servers', 'agents', 'test_cases', 'scenarios'])
|
|
1097
|
+
.enum(['all', 'servers', 'agents', 'browser_providers', 'test_cases', 'scenarios'])
|
|
1085
1098
|
.optional()
|
|
1086
1099
|
.describe('Which library category to list. Defaults to all.'),
|
|
1087
1100
|
includeContent: z
|
|
@@ -1091,15 +1104,16 @@ export function registerTools(server) {
|
|
|
1091
1104
|
}
|
|
1092
1105
|
}, async ({ kind, includeContent }) => {
|
|
1093
1106
|
return withToolHandling(async () => {
|
|
1094
|
-
const root = resolveBundleRoot();
|
|
1095
|
-
const data = readLibrary(root, Boolean(includeContent));
|
|
1096
1107
|
const selectedKind = kind ?? 'all';
|
|
1108
|
+
const root = resolveBundleRoot();
|
|
1109
|
+
const data = readLibrary(root, Boolean(includeContent), selectedKind);
|
|
1097
1110
|
const structured = selectedKind === 'all'
|
|
1098
1111
|
? data
|
|
1099
1112
|
: {
|
|
1100
1113
|
bundleRoot: data.bundleRoot,
|
|
1101
1114
|
servers: selectedKind === 'servers' ? data.servers : [],
|
|
1102
1115
|
agents: selectedKind === 'agents' ? data.agents : [],
|
|
1116
|
+
browser_providers: selectedKind === 'browser_providers' ? data.browser_providers : [],
|
|
1103
1117
|
test_cases: selectedKind === 'test_cases' ? data.test_cases : [],
|
|
1104
1118
|
scenarios: selectedKind === 'scenarios' ? data.scenarios : []
|
|
1105
1119
|
};
|
|
@@ -1107,10 +1121,10 @@ export function registerTools(server) {
|
|
|
1107
1121
|
});
|
|
1108
1122
|
});
|
|
1109
1123
|
registerTool('mcplab_get_library_item', {
|
|
1110
|
-
description: 'Get a specific reusable server, agent, or scenario definition from a MCPLab library bundle and return both structured data and YAML.',
|
|
1124
|
+
description: 'Get a specific reusable server, agent, browser provider, test case, or scenario definition from a MCPLab library bundle and return both structured data and YAML.',
|
|
1111
1125
|
outputSchema: {
|
|
1112
1126
|
bundleRoot: z.string(),
|
|
1113
|
-
kind: z.enum(['servers', 'agents', 'test_cases', 'scenarios']),
|
|
1127
|
+
kind: z.enum(['servers', 'agents', 'browser_providers', 'test_cases', 'scenarios']),
|
|
1114
1128
|
id: z.string(),
|
|
1115
1129
|
file: z.string().optional(),
|
|
1116
1130
|
yaml: z.string(),
|
|
@@ -1118,7 +1132,7 @@ export function registerTools(server) {
|
|
|
1118
1132
|
},
|
|
1119
1133
|
inputSchema: {
|
|
1120
1134
|
kind: z
|
|
1121
|
-
.enum(['servers', 'agents', 'test_cases', 'scenarios'])
|
|
1135
|
+
.enum(['servers', 'agents', 'browser_providers', 'test_cases', 'scenarios'])
|
|
1122
1136
|
.describe('Library category.'),
|
|
1123
1137
|
id: z.string().describe('Entry id (for scenarios this is scenario.id, not filename).')
|
|
1124
1138
|
}
|
|
@@ -1158,9 +1172,11 @@ export function registerTools(server) {
|
|
|
1158
1172
|
inputSchema: {
|
|
1159
1173
|
id: z.string().describe('Agent id key (kebab-case recommended).'),
|
|
1160
1174
|
provider: z
|
|
1161
|
-
.enum(['openai', 'anthropic', 'azure_openai'])
|
|
1175
|
+
.enum(['openai', 'anthropic', 'azure_openai', 'azure_foundry'])
|
|
1162
1176
|
.describe('LLM provider supported by MCPLab.'),
|
|
1163
|
-
model: z
|
|
1177
|
+
model: z
|
|
1178
|
+
.string()
|
|
1179
|
+
.describe('Model id or deployment name (for Azure OpenAI and Azure Foundry).'),
|
|
1164
1180
|
temperature: z.number().optional().describe('Sampling temperature.'),
|
|
1165
1181
|
max_tokens: z.number().int().positive().optional().describe('Maximum output tokens.'),
|
|
1166
1182
|
system: z.string().optional().describe('Optional system prompt.')
|
|
@@ -1175,6 +1191,46 @@ export function registerTools(server) {
|
|
|
1175
1191
|
});
|
|
1176
1192
|
});
|
|
1177
1193
|
});
|
|
1194
|
+
registerTool('mcplab_generate_browser_agent_entry', {
|
|
1195
|
+
description: 'Generate an agents.yaml entry for a Rover browser agent. Browser agents reference a validated browser provider profile and are executed by Rover, not by an LLM API.',
|
|
1196
|
+
outputSchema: {
|
|
1197
|
+
id: z.string(),
|
|
1198
|
+
entry: z.object({
|
|
1199
|
+
type: z.literal('browser'),
|
|
1200
|
+
name: z.string().optional(),
|
|
1201
|
+
provider: z.string(),
|
|
1202
|
+
url: z.string(),
|
|
1203
|
+
new_conversation_between_scenarios: z.boolean().optional()
|
|
1204
|
+
}),
|
|
1205
|
+
yaml: z.string()
|
|
1206
|
+
},
|
|
1207
|
+
inputSchema: {
|
|
1208
|
+
id: z.string().describe('Browser agent id key (kebab-case recommended).'),
|
|
1209
|
+
name: z.string().optional().describe('Human-readable browser agent name.'),
|
|
1210
|
+
provider: z
|
|
1211
|
+
.string()
|
|
1212
|
+
.min(1)
|
|
1213
|
+
.describe('Provider id from browser-providers/<provider-id>.yaml.'),
|
|
1214
|
+
url: z.string().url().describe('Browser origin where Rover should execute the agent.'),
|
|
1215
|
+
new_conversation_between_scenarios: z
|
|
1216
|
+
.boolean()
|
|
1217
|
+
.optional()
|
|
1218
|
+
.describe('Whether Rover should start a new conversation between scenarios.')
|
|
1219
|
+
}
|
|
1220
|
+
}, async ({ id, name, provider, url, new_conversation_between_scenarios }) => withToolHandling(async () => {
|
|
1221
|
+
const entry = removeUndefined({
|
|
1222
|
+
type: 'browser',
|
|
1223
|
+
name,
|
|
1224
|
+
provider,
|
|
1225
|
+
url,
|
|
1226
|
+
new_conversation_between_scenarios
|
|
1227
|
+
});
|
|
1228
|
+
return ok(`Generated browser agent entry '${id}'`, {
|
|
1229
|
+
id,
|
|
1230
|
+
entry,
|
|
1231
|
+
yaml: stringifyYaml({ [id]: entry }).trimEnd()
|
|
1232
|
+
});
|
|
1233
|
+
}));
|
|
1178
1234
|
registerTool('mcplab_create_test_case', {
|
|
1179
1235
|
description: 'Create one reviewed MCPLab Test Case in mcplab/test-cases/. Put evaluation checks in the eval object for full support of response, tool-input, and agent assertions. Simple shorthand fields are supported for basic cases; do not provide a shorthand field together with its equivalent eval field. This is a scoped YAML write and refuses duplicate IDs.',
|
|
1180
1236
|
outputSchema: { id: z.string(), path: z.string(), test_case: ScenarioEntrySchema },
|
|
@@ -1529,7 +1585,7 @@ export function registerTools(server) {
|
|
|
1529
1585
|
const loaded = loadConfig(resolve(config_path), {
|
|
1530
1586
|
bundleRoot
|
|
1531
1587
|
});
|
|
1532
|
-
const mergedConfig = mergeLibraryServersAndAgents(loaded.config, bundleRoot);
|
|
1588
|
+
const mergedConfig = mergeLibraryServersAndAgents(loaded.config, readLibraryAgentsAndServers(bundleRoot));
|
|
1533
1589
|
const selected = selectScenarios(mergedConfig, scenario_id);
|
|
1534
1590
|
const summary = summarizeConfig(selected);
|
|
1535
1591
|
return ok(`Validated config ${config_path}`, {
|
|
@@ -1556,7 +1612,7 @@ export function registerTools(server) {
|
|
|
1556
1612
|
scenarios: z.array(z.object({
|
|
1557
1613
|
scenario_id: z.string(),
|
|
1558
1614
|
agent: z.string(),
|
|
1559
|
-
pass_rate: z.number(),
|
|
1615
|
+
pass_rate: z.number().nullable(),
|
|
1560
1616
|
tool_usage_frequency: z.record(z.number())
|
|
1561
1617
|
})),
|
|
1562
1618
|
effective_server_overrides: z.record(z.array(z.string())),
|
|
@@ -1586,7 +1642,8 @@ export function registerTools(server) {
|
|
|
1586
1642
|
const loaded = loadConfig(resolve(config_path), {
|
|
1587
1643
|
bundleRoot
|
|
1588
1644
|
});
|
|
1589
|
-
const
|
|
1645
|
+
const libraries = readLibraryAgentsAndServers(bundleRoot);
|
|
1646
|
+
const mergedConfig = mergeLibraryServersAndAgents(loaded.config, libraries);
|
|
1590
1647
|
const selected = selectScenarios(mergedConfig, scenario_id);
|
|
1591
1648
|
const runtimeOverridden = applyRuntimeServerOverrides(selected, {
|
|
1592
1649
|
serverOverrideAll: server_override_all,
|
|
@@ -1594,13 +1651,19 @@ export function registerTools(server) {
|
|
|
1594
1651
|
});
|
|
1595
1652
|
const effectiveConfigHash = hashConfig(runtimeOverridden);
|
|
1596
1653
|
const executable = expandConfigForAgents(runtimeOverridden, runtimeOverridden.run_defaults?.selected_agents);
|
|
1654
|
+
const workspaceSettings = loadWorkspaceSettingsOverrides(bundleRoot);
|
|
1597
1655
|
const { runDir, results } = await runAll(executable, {
|
|
1598
1656
|
runsPerScenario: runs_per_scenario ?? 1,
|
|
1599
1657
|
scenarioId: scenario_id,
|
|
1600
1658
|
configHash: effectiveConfigHash,
|
|
1601
1659
|
cliVersion: `mcplab-mcp-server/${SERVER_VERSION}`,
|
|
1602
1660
|
runsDir: resolveRunsDir(),
|
|
1603
|
-
cwd: process.cwd()
|
|
1661
|
+
cwd: process.cwd(),
|
|
1662
|
+
...resolveWorkspaceEvaluation({
|
|
1663
|
+
agents: libraries.agents,
|
|
1664
|
+
scenarios: executable.scenarios,
|
|
1665
|
+
settings: toWorkspaceEvaluationSettings(workspaceSettings)
|
|
1666
|
+
})
|
|
1604
1667
|
});
|
|
1605
1668
|
const reportHtml = renderReport(results);
|
|
1606
1669
|
const allRuns = results.scenarios.flatMap((scenario) => scenario.runs);
|
|
@@ -1663,7 +1726,7 @@ export function registerTools(server) {
|
|
|
1663
1726
|
throw new Error('since must be earlier than or equal to until');
|
|
1664
1727
|
const matching = listRunsWithFallback(resolveRunsDir(), undefined, true).filter((entry) => {
|
|
1665
1728
|
const timestamp = String(entry.metadata && typeof entry.metadata === 'object'
|
|
1666
|
-
? entry.metadata.timestamp ?? ''
|
|
1729
|
+
? (entry.metadata.timestamp ?? '')
|
|
1667
1730
|
: '');
|
|
1668
1731
|
const time = new Date(timestamp).getTime();
|
|
1669
1732
|
if (sinceMs === undefined && untilMs === undefined)
|
|
@@ -1857,7 +1920,7 @@ export function registerTools(server) {
|
|
|
1857
1920
|
left: MetricSummarySchema,
|
|
1858
1921
|
right: MetricSummarySchema,
|
|
1859
1922
|
deltas: z.object({
|
|
1860
|
-
pass_rate: z.number(),
|
|
1923
|
+
pass_rate: z.number().nullable(),
|
|
1861
1924
|
failed_runs: z.number(),
|
|
1862
1925
|
avg_tool_calls_per_run: z.number(),
|
|
1863
1926
|
avg_tool_latency_ms: z.number().nullable()
|
|
@@ -1867,7 +1930,8 @@ export function registerTools(server) {
|
|
|
1867
1930
|
improved: z.number().int().nonnegative(),
|
|
1868
1931
|
unchanged: z.number().int().nonnegative(),
|
|
1869
1932
|
new: z.number().int().nonnegative(),
|
|
1870
|
-
missing: z.number().int().nonnegative()
|
|
1933
|
+
missing: z.number().int().nonnegative(),
|
|
1934
|
+
not_evaluated: z.number().int().nonnegative()
|
|
1871
1935
|
})
|
|
1872
1936
|
}),
|
|
1873
1937
|
regressions: z.array(CompareRowSchema),
|
|
@@ -2389,7 +2453,7 @@ export function registerTools(server) {
|
|
|
2389
2453
|
.array(z.object({
|
|
2390
2454
|
scenario_id: z.string(),
|
|
2391
2455
|
agent: z.string(),
|
|
2392
|
-
pass_rate: z.number()
|
|
2456
|
+
pass_rate: z.number().nullable()
|
|
2393
2457
|
}))
|
|
2394
2458
|
.optional()
|
|
2395
2459
|
},
|
|
@@ -2797,23 +2861,25 @@ function resolveServerOwnedRoots() {
|
|
|
2797
2861
|
function resolveBundleRoot() {
|
|
2798
2862
|
return SERVER_OWNED_ROOTS.bundleRoot;
|
|
2799
2863
|
}
|
|
2800
|
-
function mergeLibraryServersAndAgents(config,
|
|
2801
|
-
const libraries = readLibraryAgentsAndServers(bundleRoot);
|
|
2864
|
+
function mergeLibraryServersAndAgents(config, libraries) {
|
|
2802
2865
|
return {
|
|
2803
2866
|
...config,
|
|
2804
2867
|
agents: { ...libraries.agents, ...config.agents },
|
|
2805
2868
|
servers: { ...libraries.servers, ...config.servers }
|
|
2806
2869
|
};
|
|
2807
2870
|
}
|
|
2808
|
-
function readLibrary(bundleRoot, includeContent) {
|
|
2871
|
+
function readLibrary(bundleRoot, includeContent, kind = 'all') {
|
|
2809
2872
|
const serversPath = join(bundleRoot, 'servers.yaml');
|
|
2810
2873
|
const agentsPath = join(bundleRoot, 'agents.yaml');
|
|
2811
2874
|
const scenariosDir = resolveScenarioLibraryDir(bundleRoot).path;
|
|
2812
2875
|
const servers = existsSync(serversPath)
|
|
2813
|
-
? parseYaml(readFileSync(serversPath, 'utf8')) ?? {}
|
|
2876
|
+
? (parseYaml(readFileSync(serversPath, 'utf8')) ?? {})
|
|
2814
2877
|
: {};
|
|
2815
2878
|
const agents = existsSync(agentsPath)
|
|
2816
|
-
? parseYaml(readFileSync(agentsPath, 'utf8')) ?? {}
|
|
2879
|
+
? (parseYaml(readFileSync(agentsPath, 'utf8')) ?? {})
|
|
2880
|
+
: {};
|
|
2881
|
+
const browserProviders = kind === 'all' || kind === 'browser_providers'
|
|
2882
|
+
? mergeBrowserProviderProfiles(readBrowserProviderProfiles(bundleRoot))
|
|
2817
2883
|
: {};
|
|
2818
2884
|
const scenarioEntries = [];
|
|
2819
2885
|
if (existsSync(scenariosDir)) {
|
|
@@ -2845,29 +2911,57 @@ function readLibrary(bundleRoot, includeContent) {
|
|
|
2845
2911
|
id,
|
|
2846
2912
|
...(includeContent ? { entry: agents[id] } : {})
|
|
2847
2913
|
})),
|
|
2914
|
+
browser_providers: Object.keys(browserProviders)
|
|
2915
|
+
.sort()
|
|
2916
|
+
.map((id) => ({
|
|
2917
|
+
id,
|
|
2918
|
+
...(includeContent
|
|
2919
|
+
? { entry: browserProviders[id] }
|
|
2920
|
+
: {})
|
|
2921
|
+
})),
|
|
2848
2922
|
test_cases: scenarioEntries,
|
|
2849
2923
|
scenarios: scenarioEntries
|
|
2850
2924
|
};
|
|
2851
2925
|
return out;
|
|
2852
2926
|
}
|
|
2853
2927
|
function getLibraryItem(bundleRoot, kind, id) {
|
|
2854
|
-
if (kind === 'servers' || kind === 'agents') {
|
|
2855
|
-
const
|
|
2928
|
+
if (kind === 'servers' || kind === 'agents' || kind === 'browser_providers') {
|
|
2929
|
+
const directory = kind === 'browser_providers' ? join(bundleRoot, 'browser-providers') : null;
|
|
2930
|
+
const builtIn = kind === 'browser_providers' && Object.hasOwn(DEFAULT_BROWSER_PROVIDER_PROFILES, id)
|
|
2931
|
+
? DEFAULT_BROWSER_PROVIDER_PROFILES[id]
|
|
2932
|
+
: undefined;
|
|
2933
|
+
const file = directory
|
|
2934
|
+
? findBrowserProviderFile(directory, id)
|
|
2935
|
+
: join(bundleRoot, `${kind}.yaml`);
|
|
2936
|
+
if (builtIn && !existsSync(file)) {
|
|
2937
|
+
const content = structuredClone(builtIn);
|
|
2938
|
+
return {
|
|
2939
|
+
bundleRoot,
|
|
2940
|
+
kind,
|
|
2941
|
+
id,
|
|
2942
|
+
yaml: serializeBrowserProviderProfile(content).trimEnd(),
|
|
2943
|
+
content: content
|
|
2944
|
+
};
|
|
2945
|
+
}
|
|
2856
2946
|
if (!existsSync(file)) {
|
|
2857
2947
|
throw new Error(`Library file not found: ${file}`);
|
|
2858
2948
|
}
|
|
2859
2949
|
const raw = readFileSync(file, 'utf8');
|
|
2860
2950
|
const parsed = parseYaml(raw) ?? {};
|
|
2861
|
-
if (!(id in parsed)) {
|
|
2951
|
+
if (kind !== 'browser_providers' && !(id in parsed)) {
|
|
2862
2952
|
throw new Error(`'${id}' not found in ${file}`);
|
|
2863
2953
|
}
|
|
2864
|
-
const
|
|
2954
|
+
const normalizedEntry = kind === 'browser_providers' ? readBrowserProviderProfiles(bundleRoot)[id] : parsed[id];
|
|
2955
|
+
if (!normalizedEntry)
|
|
2956
|
+
throw new Error(`'${id}' not found in ${file}`);
|
|
2865
2957
|
return {
|
|
2866
2958
|
bundleRoot,
|
|
2867
2959
|
kind,
|
|
2868
2960
|
id,
|
|
2869
|
-
yaml:
|
|
2870
|
-
|
|
2961
|
+
yaml: kind === 'browser_providers'
|
|
2962
|
+
? raw.trimEnd()
|
|
2963
|
+
: stringifyYaml({ [id]: normalizedEntry }).trimEnd(),
|
|
2964
|
+
content: normalizedEntry
|
|
2871
2965
|
};
|
|
2872
2966
|
}
|
|
2873
2967
|
const dir = resolveScenarioLibraryDir(bundleRoot).path;
|
|
@@ -2892,6 +2986,14 @@ function getLibraryItem(bundleRoot, kind, id) {
|
|
|
2892
2986
|
}
|
|
2893
2987
|
throw new Error(`Scenario '${id}' not found in ${dir}`);
|
|
2894
2988
|
}
|
|
2989
|
+
function findBrowserProviderFile(directory, id) {
|
|
2990
|
+
if (!existsSync(directory))
|
|
2991
|
+
return join(directory, `${id}.yaml`);
|
|
2992
|
+
const file = readdirSync(directory)
|
|
2993
|
+
.filter((name) => name.endsWith('.yaml') || name.endsWith('.yml'))
|
|
2994
|
+
.find((name) => basename(name, extname(name)) === id);
|
|
2995
|
+
return join(directory, file ?? `${id}.yaml`);
|
|
2996
|
+
}
|
|
2895
2997
|
function listEvaluationConfigs(params) {
|
|
2896
2998
|
const evalsDir = join(resolveBundleRoot(), 'evals');
|
|
2897
2999
|
const files = [];
|
|
@@ -3762,9 +3864,9 @@ function truncate(text, maxChars) {
|
|
|
3762
3864
|
return `${text.slice(0, maxChars)}\n...[truncated ${text.length - maxChars} chars]`;
|
|
3763
3865
|
}
|
|
3764
3866
|
export async function handleMcplabMcpHttpRequest(req, res, sessions, options) {
|
|
3765
|
-
await handleHttpRequest(req, res, sessions, options?.path ?? DEFAULT_MCP_PATH);
|
|
3867
|
+
await handleHttpRequest(req, res, sessions, options?.path ?? DEFAULT_MCP_PATH, options?.logger ?? console);
|
|
3766
3868
|
}
|
|
3767
|
-
async function handleHttpRequest(req, res, sessions, mcpPath) {
|
|
3869
|
+
async function handleHttpRequest(req, res, sessions, mcpPath, logger = console) {
|
|
3768
3870
|
const method = req.method ?? 'GET';
|
|
3769
3871
|
const pathname = new URL(req.url ?? '/', `http://${req.headers.host ?? 'localhost'}`).pathname;
|
|
3770
3872
|
if (pathname === '/' && method === 'GET') {
|
|
@@ -3796,14 +3898,25 @@ async function handleHttpRequest(req, res, sessions, mcpPath) {
|
|
|
3796
3898
|
return;
|
|
3797
3899
|
}
|
|
3798
3900
|
if (isInitializeRequest(body)) {
|
|
3901
|
+
const clientInfo = body.params && typeof body.params === 'object' && 'clientInfo' in body.params
|
|
3902
|
+
? body.params.clientInfo
|
|
3903
|
+
: undefined;
|
|
3904
|
+
const clientName = clientInfo && typeof clientInfo === 'object' && 'name' in clientInfo
|
|
3905
|
+
? String(clientInfo.name)
|
|
3906
|
+
: 'unknown';
|
|
3907
|
+
const clientVersion = clientInfo && typeof clientInfo === 'object' && 'version' in clientInfo
|
|
3908
|
+
? String(clientInfo.version)
|
|
3909
|
+
: undefined;
|
|
3799
3910
|
let runtime;
|
|
3800
3911
|
const transport = new StreamableHTTPServerTransport({
|
|
3801
3912
|
sessionIdGenerator: () => randomUUID(),
|
|
3802
3913
|
onsessioninitialized: (sid) => {
|
|
3803
3914
|
sessions.set(sid, runtime);
|
|
3915
|
+
logger.log(`[mcplab-mcp] client connected: ${clientName}${clientVersion ? `/${clientVersion}` : ''} (session ${sid})`);
|
|
3804
3916
|
},
|
|
3805
3917
|
onsessionclosed: (sid) => {
|
|
3806
3918
|
sessions.delete(sid);
|
|
3919
|
+
logger.log(`[mcplab-mcp] client disconnected: session ${sid}`);
|
|
3807
3920
|
}
|
|
3808
3921
|
});
|
|
3809
3922
|
const mcpServer = createConfiguredServer();
|