-
Notifications
You must be signed in to change notification settings - Fork 340
Expand file tree
/
Copy pathcontext.ts
More file actions
122 lines (109 loc) · 3.56 KB
/
context.ts
File metadata and controls
122 lines (109 loc) · 3.56 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
import type { Stagehand } from "@browserbasehq/stagehand";
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import type { Config } from "../config.d.ts";
import { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
import { listResources, readResource } from "./mcp/resources.js";
import { SessionManager } from "./sessionManager.js";
import type { MCPTool } from "./types/types.js";
/**
* MCP Server Context
*
* Central controller that connects the MCP server infrastructure with browser automation capabilities,
* managing server instances, browser sessions, tool execution, and resource access.
*/
export class Context {
public readonly config: Config;
private server: Server;
private sessionManager: SessionManager;
// currentSessionId is a getter that delegates to SessionManager to ensure synchronization
// This prevents desync between Context and SessionManager session tracking
public get currentSessionId(): string {
return this.sessionManager.getActiveSessionId();
}
constructor(server: Server, config: Config, contextId?: string) {
this.server = server;
this.config = config;
this.sessionManager = new SessionManager(contextId);
}
public getServer(): Server {
return this.server;
}
public getSessionManager(): SessionManager {
return this.sessionManager;
}
/**
* Gets the Stagehand instance for the current session from SessionManager
*/
public async getStagehand(
sessionId: string = this.currentSessionId,
): Promise<Stagehand> {
const session = await this.sessionManager.getSession(
sessionId,
this.config,
);
if (!session) {
throw new Error(`No session found for ID: ${sessionId}`);
}
return session.stagehand;
}
async run(tool: MCPTool, args: unknown): Promise<CallToolResult> {
try {
console.error(
`Executing tool: ${tool.schema.name} with args: ${JSON.stringify(args)}`,
);
// Check if this tool has a handle method (new tool system)
if ("handle" in tool && typeof tool.handle === "function") {
const toolResult = await tool.handle(this, args);
if (toolResult?.action) {
const actionResult = await toolResult.action();
const content = actionResult?.content || [];
return {
content: Array.isArray(content)
? content
: [{ type: "text", text: "Action completed successfully." }],
isError: false,
};
} else {
return {
content: [
{
type: "text",
text: `${tool.schema.name} completed successfully.`,
},
],
isError: false,
};
}
} else {
// Fallback for any legacy tools without handle method
throw new Error(
`Tool ${tool.schema.name} does not have a handle method`,
);
}
} catch (error) {
const errorMessage =
error instanceof Error ? error.message : String(error);
console.error(
`Tool ${tool.schema?.name || "unknown"} failed: ${errorMessage}`,
);
return {
content: [{ type: "text", text: `Error: ${errorMessage}` }],
isError: true,
};
}
}
/**
* List resources
* Documentation: https://modelcontextprotocol.io/docs/concepts/resources
*/
listResources() {
return listResources();
}
/**
* Read a resource by URI
* Documentation: https://modelcontextprotocol.io/docs/concepts/resources
*/
readResource(uri: string) {
return readResource(uri);
}
}