-
Notifications
You must be signed in to change notification settings - Fork 413
Expand file tree
/
Copy pathbrowser.app.ts
More file actions
122 lines (104 loc) · 3.55 KB
/
browser.app.ts
File metadata and controls
122 lines (104 loc) · 3.55 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 OAuthProvider from '@cloudflare/workers-oauth-provider'
import { McpAgent } from 'agents/mcp'
import {
createAuthHandlers,
handleTokenExchangeCallback,
} from '@repo/mcp-common/src/cloudflare-oauth-handler'
import { handleDevMode } from '@repo/mcp-common/src/dev-mode'
import { getUserDetails, UserDetails } from '@repo/mcp-common/src/durable-objects/user_details.do'
import { getEnv } from '@repo/mcp-common/src/env'
import { RequiredScopes } from '@repo/mcp-common/src/scopes'
import { CloudflareMCPServer } from '@repo/mcp-common/src/server'
import { registerAccountTools } from '@repo/mcp-common/src/tools/account.tools'
import { MetricsTracker } from '../../../packages/mcp-observability/src'
import { registerBrowserTools } from './tools/browser.tools'
import type { AuthProps } from '@repo/mcp-common/src/cloudflare-oauth-handler'
import type { Env } from './browser.context'
const env = getEnv<Env>()
export { UserDetails }
const metrics = new MetricsTracker(env.MCP_METRICS, {
name: env.MCP_SERVER_NAME,
version: env.MCP_SERVER_VERSION,
})
// Context from the auth process, encrypted & stored in the auth token
// and provided to the DurableMCP as this.props
type Props = AuthProps
type State = { activeAccountId: string | null }
export class BrowserMCP extends McpAgent<Env, State, Props> {
_server: CloudflareMCPServer | undefined
set server(server: CloudflareMCPServer) {
this._server = server
}
get server(): CloudflareMCPServer {
if (!this._server) {
throw new Error('Tried to access server before it was initialized')
}
return this._server
}
constructor(ctx: DurableObjectState, env: Env) {
super(ctx, env)
}
async init() {
this.server = new CloudflareMCPServer({
userId: this.props.user.id,
wae: this.env.MCP_METRICS,
serverInfo: {
name: this.env.MCP_SERVER_NAME,
version: this.env.MCP_SERVER_VERSION,
},
})
registerAccountTools(this)
// Register Cloudflare Log Push tools
registerBrowserTools(this)
}
async getActiveAccountId() {
try {
// Get UserDetails Durable Object based off the userId and retrieve the activeAccountId from it
// we do this so we can persist activeAccountId across sessions
const userDetails = getUserDetails(env, this.props.user.id)
return await userDetails.getActiveAccountId()
} catch (e) {
this.server.recordError(e)
return null
}
}
async setActiveAccountId(accountId: string) {
try {
const userDetails = getUserDetails(env, this.props.user.id)
await userDetails.setActiveAccountId(accountId)
} catch (e) {
this.server.recordError(e)
}
}
}
const BrowserScopes = {
...RequiredScopes,
'account:read': 'See your account info such as account details, analytics, and memberships.',
'browser:write': 'Grants write level access to Browser Rendering.',
} as const
export default {
fetch: async (req: Request, env: Env, ctx: ExecutionContext) => {
if (env.ENVIRONMENT === 'development' && env.DEV_DISABLE_OAUTH === 'true') {
return await handleDevMode(BrowserMCP, req, env, ctx)
}
return new OAuthProvider({
apiHandlers: {
'/mcp': BrowserMCP.serve('/mcp'),
'/sse': BrowserMCP.serveSSE('/sse'),
},
// @ts-ignore
defaultHandler: createAuthHandlers({ scopes: BrowserScopes, metrics }),
authorizeEndpoint: '/oauth/authorize',
tokenEndpoint: '/token',
tokenExchangeCallback: (options) =>
handleTokenExchangeCallback(
options,
env.CLOUDFLARE_CLIENT_ID,
env.CLOUDFLARE_CLIENT_SECRET
),
// Cloudflare access token TTL
accessTokenTTL: 3600,
clientRegistrationEndpoint: '/register',
}).fetch(req, env, ctx)
},
}