openai/gpt-oss

Public

mirrored fromhttps://github.com/openai/gpt-ossAvailable

CodeCommitsIssuesPull requestsActionsInsightsSecurity
main

Branches

Tags

  • No tags available.
0Branches0Tags
Go to file
Add file
Code

Clone

HTTPS

Download ZIP

gpt-oss-mcp-server/browser_server.py

120lines · modecode

1import os
2from collections.abc import AsyncIterator
3from contextlib import asynccontextmanager
4from dataclasses import dataclass, field
5from typing import Union, Optional
6
7from mcp.server.fastmcp import Context, FastMCP
8from gpt_oss.tools.simple_browser import SimpleBrowserTool
9from gpt_oss.tools.simple_browser.backend import YouComBackend, ExaBackend
10
11@dataclass
12class AppContext:
13 browsers: dict[str, SimpleBrowserTool] = field(default_factory=dict)
14
15 def create_or_get_browser(self, session_id: str) -> SimpleBrowserTool:
16 if session_id not in self.browsers:
17 tool_backend = os.getenv("BROWSER_BACKEND", "youcom")
18 if tool_backend == "youcom":
19 backend = YouComBackend(source="web")
20 elif tool_backend == "exa":
21 backend = ExaBackend(source="web")
22 else:
23 raise ValueError(f"Invalid tool backend: {tool_backend}")
24 self.browsers[session_id] = SimpleBrowserTool(backend=backend)
25 return self.browsers[session_id]
26
27 def remove_browser(self, session_id: str) -> None:
28 self.browsers.pop(session_id, None)
29
30
31@asynccontextmanager
32async def app_lifespan(_server: FastMCP) -> AsyncIterator[AppContext]:
33 yield AppContext()
34
35
36# Pass lifespan to server
37mcp = FastMCP(
38 name="browser",
39 instructions=r"""
40Tool for browsing.
41The `cursor` appears in brackets before each browsing display: `[{cursor}]`.
42Cite information from the tool using the following format:
43`【{cursor}†L{line_start}(-L{line_end})?】`, for example: `【6†L9-L11】` or `【8†L3】`.
44Do not quote more than 10 words directly from the tool output.
45sources=web
46""".strip(),
47 lifespan=app_lifespan,
48 port=8001,
49)
50
51
52@mcp.tool(
53 name="search",
54 title="Search for information",
55 description=
56 "Searches for information related to `query` and displays `topn` results.",
57)
58async def search(ctx: Context,
59 query: str,
60 topn: int = 10,
61 source: Optional[str] = None) -> str:
62 """Search for information related to a query"""
63 browser = ctx.request_context.lifespan_context.create_or_get_browser(
64 ctx.client_id)
65 messages = []
66 async for message in browser.search(query=query, topn=topn, source=source):
67 if message.content and hasattr(message.content[0], 'text'):
68 messages.append(message.content[0].text)
69 return "\n".join(messages)
70
71
72@mcp.tool(
73 name="open",
74 title="Open a link or page",
75 description="""
76Opens the link `id` from the page indicated by `cursor` starting at line number `loc`, showing `num_lines` lines.
77Valid link ids are displayed with the formatting: `【{id}†.*】`.
78If `cursor` is not provided, the most recent page is implied.
79If `id` is a string, it is treated as a fully qualified URL associated with `source`.
80If `loc` is not provided, the viewport will be positioned at the beginning of the document or centered on the most relevant passage, if available.
81Use this function without `id` to scroll to a new location of an opened page.
82""".strip(),
83)
84async def open_link(ctx: Context,
85 id: Union[int, str] = -1,
86 cursor: int = -1,
87 loc: int = -1,
88 num_lines: int = -1,
89 view_source: bool = False,
90 source: Optional[str] = None) -> str:
91 """Open a link or navigate to a page location"""
92 browser = ctx.request_context.lifespan_context.create_or_get_browser(
93 ctx.client_id)
94 messages = []
95 async for message in browser.open(id=id,
96 cursor=cursor,
97 loc=loc,
98 num_lines=num_lines,
99 view_source=view_source,
100 source=source):
101 if message.content and hasattr(message.content[0], 'text'):
102 messages.append(message.content[0].text)
103 return "\n".join(messages)
104
105
106@mcp.tool(
107 name="find",
108 title="Find pattern in page",
109 description=
110 "Finds exact matches of `pattern` in the current page, or the page given by `cursor`.",
111)
112async def find_pattern(ctx: Context, pattern: str, cursor: int = -1) -> str:
113 """Find exact matches of a pattern in the current page"""
114 browser = ctx.request_context.lifespan_context.create_or_get_browser(
115 ctx.client_id)
116 messages = []
117 async for message in browser.find(pattern=pattern, cursor=cursor):
118 if message.content and hasattr(message.content[0], 'text'):
119 messages.append(message.content[0].text)
120 return "\n".join(messages)
121