关于 Doroseek
Doroseek 是基于 Deno 和 Fresh 的应用,可管理兼容 OpenAI 的端点与 API 密钥,也可作为 MCP 服务和代理。它还提供本地服务隧道、实时通信房间和小文件分享功能。
主要功能
- 01管理端点和 API 密钥
- 02为模型设置别名
- 03提供内置 MCP 服务
- 04代理其他 MCP 服务
- 05隧道转发本地服务并支持通信房间
- 06通过 URL 分享小文件
适用场景
- 01访问多个兼容 OpenAI 的端点
- 02让客户端连接其他 MCP 服务
- 03通过隧道公开本地 Web 服务
- 04分享小文件或启用房间消息通信
MCP 相关
Doroseek 提供内置 MCP 服务,也可代理其他 MCP 服务,并通过 SSE 路由建立连接。其 Agent Mode 可连接远程 Doroseek 实例以转发本地服务。
配置
本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。
我们还没有获取到这个服务的安装包或远程地址,请按项目 README 中的说明安装。
工具
本地服务需要在你的电脑上运行后才能读取工具,维护者暂未提供工具列表。
详细介绍
Doroseek
使用 Deno 和 Fresh 构建的简单 AI 应用。
- 使用相同的基础 URL 和 API 密钥访问所有 OpenAI Compatible 端点。你也可以与他人共享
DoroseekAPI 密钥,以便他们访问这些端点。 - 作为 MCP 服务,内置多个服务。
- 作为 MCP 代理服务,连接到其他 MCP 服务。

功能
- OpenAI Compatible 端点
- 管理端点和 API 密钥
- 生成
DoroseekAPI 密钥 - 为模型分配别名
- 路由
- 管理路由:
/{key} - OpenAI Compatible 路由:
/api
- 管理路由:
- MCP SSE 服务
- sequentialthinking - Sequential Thinking
- think - Think Tool MCP Server
- 路由
/mcp/{server}/sse?apiKey={apiKey}
- MCP 代理服务
- 路由
- stdio:
/mcp/proxy/sse?apiKey={apiKey}&transport=stdio&command=&args=&env= - sse:
/mcp/proxy/sse?apiKey={apiKey}&transport=sse&url=
- stdio:
- 示例
- Sequential Thinking:
/mcp/proxy/sse?apiKey={apiKey}&transport=stdio&command=npx&args=-y @modelcontextprotocol/server-sequential-thinking&env={}
- Sequential Thinking:
- 路由
新的核心功能(基于 MCP)
Doroseek 现已包含多个强大的基于 MCP 的服务,可用于高级场景:
- **内网穿透(隧道):**将本地服务暴露到互联网。
- **多用户通信室:**支持多个客户端之间实时通信。
- **资源共享:**上传和共享小型文件或数据片段。
这些功能利用 WebSocket 实现实时通信,并在适用时使用 Deno KV 进行持久化。
1. 内网穿透(隧道)
概念:
此功能可让你通过与 Doroseek 实例建立的安全隧道,将私有网络中运行的服务(例如本地开发服务器、数据库或任何 TCP/HTTP 服务)暴露到公共互联网。
本地代理:
要使用此功能,必须在可访问私有服务的计算机上运行本地代理(客户端软件)。Doroseek 本身也可以运行在此“代理模式”下。有关如何将 Doroseek 配置并作为本地代理运行的详细信息,请参阅下文的代理模式部分。本地代理负责连接远程 Doroseek 实例(充当中继)、管理隧道,以及在中继与本地服务之间转发流量。
注册流程(代理到中继):
-
**连接:**本地代理与
DoroseekMCP 服务建立 WebSocket 连接:wss://your-doroseek-instance/mcp/tunnel/register?apiKey=<your_api_key>注册时必须提供 API 密钥。
-
**注册消息:**建立 WebSocket 连接后,代理会发送一条
register消息,详细说明它希望暴露的服务。 代理发送的register消息示例:{ "type": "register", "data": { "services": [ { "type": "http", "local_port": 3000, "subdomain_or_path": "my-web-app" }, { "type": "http", "local_port": 8080, "subdomain_or_path": "api-service" } // Other types like 'tcp' might be supported by the agent protocol in the future. ] } } -
**服务器响应:**注册成功后,
Doroseek会返回一条registered消息,其中包含唯一的tunnelId和用于访问隧道服务的公共基础 URL。 服务器发送的registered消息示例:{ "type": "registered", "data": { "tunnelId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", "public_base_url": "https://your-doroseek-instance/t/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" } }
访问隧道服务:
隧道建立后,可通过公共基础 URL(public_base_url)以及注册时定义的 subdomain_or_path 拼接出的公共 URL 访问本地服务。例如,如果 your-doroseek-instance 为 example.com,且某服务注册时使用了 subdomain_or_path: 'my-web-app',则它可能可通过以下地址访问:
https://example.com/t/<tunnelId>/my-web-app/...
具体 URL 结构取决于公共路由(/t/:tunnelId/...)如何配置,以映射到特定服务。
本地代理职责(概要):
- 发起并维持到
/mcp/tunnel/register的 WebSocket 连接。 - 发送包含服务定义的
register消息。 - 监听
Doroseek通过 WebSocket 发送的httpRequest消息。每条httpRequest消息都包含:requestId:请求的唯一 ID。data:包含传入公共请求的method、path、headers和body的对象。
- 收到
httpRequest后,代理会向其本地服务发起相应请求(例如http://localhost:3000/some/path)。 - 本地服务响应后,代理会通过 WebSocket 向
Doroseek发送httpResponse消息,其中包含原始requestId和响应详情(状态、标头、正文)。 - (目前,
Doroseek面向公共网络的隧道路由中仅预留了通过隧道进行 WebSocket 代理的功能,计划在未来增强。)
2. 多用户通信房间
概念: 在多个客户端之间实现实时双向通信。可将其用作 Web 应用中实时聊天、协作编辑或其他多用户交互体验等功能的后端。
连接到房间:
客户端使用 WebSocket 连接到房间。roomId 在路径中指定。
- WebSocket 端点:
wss://your-doroseek-instance/mcp/room/:roomId?apiKey=<optional_api_key> - 身份验证:
- **匿名访问:**目前,客户端无需 API 密钥即可连接。
- **通过 API 密钥验证的访问:**如果在查询字符串中提供了
apiKey,系统会对其进行验证。这样便可在需要时将用户与 API 密钥所有者关联起来。
核心功能:
- **加入房间:**用户成功建立与房间端点的 WebSocket 连接后,即加入房间。
- 加入时,服务器会向该用户发送
ServerRoomInfoMessage,其中包含房间内现有用户的列表。 - 房间内的其他用户会收到
ServerUserJoinedMessage。
- 加入时,服务器会向该用户发送
- **离开房间:**用户的 WebSocket 连接关闭时,该用户会自动从房间中移除。
- 房间内的其他用户会收到
ServerUserLeftMessage。
- 房间内的其他用户会收到
- **发送聊天消息:**客户端通过 WebSocket 发送聊天消息。
客户端发送的
ClientChatMessage示例:{ "type": "chatMessage", "payload": { "roomId": "your-target-room-id", // Though roomId is in path, can be in payload "message": "Hello everyone!" } } - **接收消息:**客户端监听服务器发送的消息。
ServerChatMessage:由房间内其他用户发送的聊天消息。{ "type": "chatMessage", "payload": { "roomId": "the-room-id", "fromUserId": "user-id-of-sender", "message": "Hello everyone!", "timestamp": "2023-01-01T12:00:00.000Z" } }ServerUserJoinedMessage:通知有新用户加入。ServerUserLeftMessage:通知有用户离开。
客户端交互概念示例(JavaScript):
const roomId = "my-chat-room";
const apiKey = "your-optional-api-key"; // Or leave undefined for anonymous
const socket = new WebSocket(`wss://your-doroseek-instance/mcp/room/${roomId}?apiKey=${apiKey}`);
socket.onopen = () => {
console.log("Connected to room:", roomId);
// Send a chat message
socket.send(JSON.stringify({
type: "chatMessage",
payload: { roomId, message: "Hi from client!" }
}));
};
socket.onmessage = (event) => {
const serverMessage = JSON.parse(event.data);
console.log("Received message:", serverMessage);
if (serverMessage.type === "chatMessage") {
// Display chat: serverMessage.payload.fromUserId, serverMessage.payload.message
} else if (serverMessage.type === "userJoined") {
// Update user list
} // etc.
};
socket.onclose = () => {
console.log("Disconnected from room:", roomId);
};
socket.onerror = (error) => {
console.error("WebSocket error:", error);
};
3. 资源共享
概念:
允许用户将小型文件或数据片段(例如配置文件、JSON 数据、小图片)上传到 Doroseek,并通过唯一的公开访问 URL 分享。
上传流程(MCP WebSocket):
-
**端点:**通过 WebSocket 连接到
/mcp/fileshare/upload。wss://your-doroseek-instance/mcp/fileshare/upload?apiKey=<your_api_key>上传文件必须使用有效的 API 密钥。
-
消息流程:
- 客户端 -> 服务端:
ClientInitiateUploadMessage客户端首先发送一条消息以启动上传,并提供文件元数据。{ "type": "initiateUpload", "payload": { "filename": "config.json", "filetype": "application/json", "size": 1024 // Size in bytes } } - 服务端 -> 客户端:
ServerUploadReadyMessage如果服务端接受上传(例如,文件大小未超出限制),则会返回唯一的resourceId。{ "type": "uploadReady", "payload": { "resourceId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" } } - 客户端 -> 服务端:
ClientFileDataMessage客户端发送实际文件数据,并使用 base64 编码。{ "type": "fileData", "payload": { "resourceId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", "data": "eyJrZXkiOiAidmFsdWUifQ==" // Base64 encoded content of the file } } - 服务端 -> 客户端:
ServerUploadCompleteMessage成功保存文件数据后,服务端会确认上传完成并提供下载 URL。{ "type": "uploadComplete", "payload": { "resourceId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", "downloadUrl": "https://your-doroseek-instance/shared/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" } }
- 客户端 -> 服务端:
下载流程(HTTP GET):
- **URL 格式:**可通过向以下地址发送简单的 HTTP GET 请求来下载文件:
https://your-doroseek-instance/shared/:resourceId - **身份验证:**默认情况下,下载无需 API 密钥。只要知道链接,任何人都可以公开访问。
限制:
- 文件大小限制:此功能目前适用于小型文件。由于直接使用 Deno KV 作为文件数据存储后端,单个文件的大小实际上受限于 Deno KV 的值大小限制,通常约为 60KB。超出此限制的上传将被拒绝。未来可能会支持分块上传较大的文件。
本地运行
本节介绍如何以默认的服务端模式运行 Doroseek。如需将 Doroseek 作为 Tunneling 功能的本地 Agent 运行,请参阅Agent 模式一节。
服务端模式配置
将 .env.example 复制为 .env
# .env
ADMIN_KEY=the_admin_key_here
MCP_MAX_DURATION=60
默认情况下,项目允许任何密钥创建对应的设置。如果
需要将访问限制为特定密钥,则需要设置
ADMIN_KEY 环境变量。
MCP SSE 连接最多可保持 MCP_MAX_DURATION 秒。
如有需要,设置数据库
// services/database.ts
export const db = await Deno.openKv("./db");
以服务端模式运行
若要以默认服务端模式运行应用,请确保未将 DOROSEEK_AGENT_MODE_ENABLED 设置为 "true"。
你需要安装 Deno。然后在此仓库的根目录运行:
deno task start
Agent 模式(用于内网穿透)
Doroseek 也可以在 Agent 模式下运行。在此模式下,它不会启动 Fresh Web 服务,而是作为本地客户端 Agent 连接到远程 Doroseek 实例(充当中继服务),将私有网络中的服务暴露到互联网。
这是区别于默认服务端模式的另一种运行模式。启用并正确配置 Agent 模式后,Doroseek 将只执行 Agent 职责。
Agent 模式配置
要启用并配置 Agent 模式,需要设置以下环境变量:
-
DOROSEEK_AGENT_MODE_ENABLED:- 设置为
"true"以启用 Agent 模式。如果未设置为"true",Doroseek将尝试以服务端模式启动。
- 设置为
-
DOROSEEK_RELAY_URL:- 此 Agent 应连接的远程
Doroseek实例(充当中继服务)的 WebSocket URL。此 URL 应指向中继的隧道注册端点。 - 示例:
wss://your-remote-doroseek.com/mcp/tunnel/register
- 此 Agent 应连接的远程
-
DOROSEEK_AGENT_API_KEY:- 此 Agent 用于向远程
Doroseek中继服务进行身份验证的 API 密钥。中继服务必须能够识别并授权此 API 密钥。
- 此 Agent 用于向远程
-
DOROSEEK_AGENT_SERVICES_JSON:-
定义本地服务数组的 JSON 字符串,Agent 应通过隧道暴露这些服务。
-
数组中的每个服务对象都必须包含以下字段:
id(字符串):服务的唯一标识符(由你指定,例如"my-web")。name(字符串):易于识别的服务名称(例如"My Local Web Server")。type(字符串):服务类型。目前,Agent 的 HTTP 处理程序仅支持"http"。local_host(字符串):本地服务的主机名或 IP 地址(例如"localhost"、"127.0.0.1")。local_port(数字):本地服务运行所在的端口号(例如8080)。subdomainOrPath(字符串):中继服务将使用此字符串来构建该服务的公共 URL。字符串中不能包含/或空格。它实际上会成为中继公共隧道 URL 中的一个路径段。- 例如,如果中继提供的基础 URL 为
https://<relay-public-domain>/t/<tunnelId>,并且你将subdomainOrPath设置为"myweb",则可以通过https://<relay-public-domain>/t/<tunnelId>/myweb/...访问你的服务。
- 例如,如果中继提供的基础 URL 为
-
DOROSEEK_AGENT_SERVICES_JSON的示例值:[ { "id": "web1", "name": "My Local Web Server", "type": "http", "local_host": "localhost", "local_port": 8080, "subdomainOrPath": "myweb" }, { "id": "api2", "name": "Backend API", "type": "http", "local_host": "127.0.0.1", "local_port": 3000, "subdomainOrPath": "myapi" } ]要将其设置为环境变量,通常需要将 JSON 字符串压缩为单行(不含换行),并在某些 Shell 环境中进行正确转义。例如,在
.env文件或 Shell 中:DOROSEEK_AGENT_SERVICES_JSON='[{"id":"web1","name":"My Local Web Server","type":"http","local_host":"localhost","local_port":8080,"subdomainOrPath":"myweb"},{"id":"api2","name":"Backend API","type":"http","local_host":"127.0.0.1","local_port":3000,"subdomainOrPath":"myapi"}]'
-
在 Agent 模式下运行
- 设置上方列出的所有必需环境变量。确保
DOROSEEK_AGENT_MODE_ENABLED设置为"true"。 - 使用标准命令运行应用:
deno task start - 如果配置有效,
Doroseek将以 Agent 模式启动。你将不会看到常见的 Fresh 服务启动日志(例如,“Listening on http://localhost:8000/”)。 - 请留意表明 Agent 模式正在运行的日志消息,例如:
[Main] Doroseek starting in Agent Mode.[Agent Config] Agent configuration loaded successfully.[Agent Connector] Initialized with Relay URL: wss://your-remote-doroseek.com/mcp/tunnel/register[Agent Connector] Attempting to connect to ...[Agent Connector] WebSocket connection established.[Agent Connector] Sent registration request: ...[Agent Connector] Successfully registered. Tunnel ID: <your-tunnel-id>, Public URL: <your-public-base-url>[Main] Agent is connected and registered with the relay.[Main] Tunnel ID: <your-tunnel-id>[Main] Public Base URL: <your-public-base-url>- Service 'My Local Web Server' (web1) accessible via: <your-public-base-url>/myweb- Service 'Backend API' (api2) accessible via: <your-public-base-url>/myapi
与中继交互(Agent 视角)
- Agent(此
Doroseek实例以 Agent 模式运行)会与DOROSEEK_RELAY_URL建立 WebSocket 连接。该 URL 应为以 Server 模式运行的远程Doroseek实例的/mcp/tunnel/register端点。 - Agent 会发送一条注册消息,其中包含在
DOROSEEK_AGENT_SERVICES_JSON中定义的本地服务的详细信息。 - 注册成功后,远程中继服务会为 Agent 提供唯一的
tunnelId和public_base_url。 - 随后,当中继服务收到与该隧道匹配的公共 HTTP 请求时,会通过已建立的 WebSocket 隧道将这些请求作为
httpRequest消息转发给 Agent。 - Agent 处理这些
httpRequest消息,向已配置的本地服务发起请求,然后向中继发送httpResponse消息。 - Agent 还会以
pong消息响应中继发送的ping消息,以报告自身状态及其主要本地服务的健康状况。
隧道健康检查 API
为了监控已建立隧道的状态以及底层本地服务的状态,Doroseek 提供了一个健康检查 API 端点。
-
端点:
GET /tunnel/:tunnelId/status -
描述: 此端点允许你查询由
:tunnelId标识的特定隧道的当前状态。它会检查中继服务器到本地 Agent 的 WebSocket 连接,以及 Agent 是否能够访问已配置的本地服务。 -
身份验证: 此端点目前不需要特定的 API 密钥身份验证,但
tunnelId必须有效且为系统所知。如有需要,可以通过标准反向代理或网关机制添加访问控制。 -
响应正文(
HealthStatusReport): API 会返回具有以下结构的 JSON 对象:{ "tunnelId": "string", // 被检查的隧道 ID "tunnelStatus": "connected" | "disconnected" | "unknown", "localServiceStatus": "ok" | "error" | "unconfigured" | "timeout" | "unknown" | "agent_unresponsive", "checkedByInstanceId": "string", // 执行或报告此次检查的中继实例 ID "timestamp": "string" // 执行检查时的 ISO 8601 时间戳 }tunnelId:已检查隧道的 ID。tunnelStatus:"connected":中继服务器已与此隧道的本地 Agent 建立活动的 WebSocket 连接。"disconnected":中继服务器未与本地 Agent 建立活动的 WebSocket 连接。"unknown":无法确定隧道状态(例如,在主数据库中找不到隧道 ID)。
localServiceStatus:"ok":本地 Agent 已响应健康检查 ping,并报告其主要配置的本地服务可访问。"error":本地 Agent 已响应,但在尝试检查本地服务时报告了错误。"unconfigured":本地 Agent 已响应,但未配置要检查的本地服务,或者 Agent 无法检查该服务类型。"timeout":本地 Agent 已响应,但其检查本地服务的尝试超时。"agent_unresponsive":中继服务器已连接到 Agent,但 Agent 未在预期时间内响应健康检查 ping。"unknown":无法确定本地服务的状态(例如,因为tunnelStatus不是"connected")。
checkedByInstanceId:执行或最近一次报告健康状态的中继服务器实例的唯一 ID。适用于多实例部署。timestamp:确定健康状态时的 ISO 8601 时间戳。
-
使用示例: 请求:
GET /tunnel/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/status响应示例:
{ "tunnelId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", "tunnelStatus": "connected", "localServiceStatus": "ok", "checkedByInstanceId": "yyyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy", "timestamp": "2023-10-27T10:30:00.000Z" }另一个示例(Agent 已连接,但本地服务已停止):
{ "tunnelId": "zzzzzzzz-zzzz-zzzz-zzzz-zzzzzzzzzzzz", "tunnelStatus": "connected", "localServiceStatus": "error", "checkedByInstanceId": "wwwwwww-wwww-wwww-wwww-wwwwwwwwwwww", "timestamp": "2023-10-27T10:35:00.000Z" }
评论
一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。