junjiepro/doroseek
If you are the rightful owner of doroseek and would like to certify it and/or have it hosted online, please leave a comment on the right or send an email to henry@mcphub.com.
Doroseek is a simple AI app built with Deno and Fresh, providing OpenAI compatible endpoints and acting as an MCP server and proxy.
Doroseek
A simple AI app built with Deno and Fresh.
- Access all your OpenAI Compatible endpoints with the same base URL and API
key. And you can share the
DoroseekAPI key with others to access the endpoints. - As MCP server with several build-in servers.
- As MCP Proxy server, connect to other MCP server.

Features
- OpenAI Compatible endpoints
- Manage endpoints and API keys
- Generate
DoroseekAPI keys - Assign alias to models
- route
- manage route:
/{key} - OpenAI Compatible route:
/api
- manage route:
- MCP SSE Server
- sequentialthinking - Sequential Thinking
- think - Think Tool MCP Server
- route
/mcp/{server}/sse?apiKey={apiKey}
- MCP Proxy Server
- route
- stdio:
/mcp/proxy/sse?apiKey={apiKey}&transport=stdio&command=&args=&env= - sse:
/mcp/proxy/sse?apiKey={apiKey}&transport=sse&url=
- stdio:
- examples
- Sequential Thinking:
/mcp/proxy/sse?apiKey={apiKey}&transport=stdio&command=npx&args=-y @modelcontextprotocol/server-sequential-thinking&env={}
- Sequential Thinking:
- route
New Core Features (MCP Based)
Doroseek now includes several powerful MCP-based services for advanced use cases:
- Intranet Penetration (Tunneling): Expose local services to the internet.
- Multi-User Communication Rooms: Enable real-time communication between multiple clients.
- Resource Sharing: Upload and share small files or data snippets.
These features leverage WebSockets for real-time communication and Deno KV for persistence where applicable.
1. Intranet Penetration (Tunneling)
Concept:
This feature allows you to expose services running on your private network (e.g., a local development server, a database, or any TCP/HTTP service) to the public internet through a secure tunnel established with your Doroseek instance.
Local Agent:
To use this feature, a local agent (client software) must be run on the machine where the private services are accessible. Doroseek itself can also run in this "Agent Mode". For details on configuring and running Doroseek as a local agent, see the Agent Mode section below. The agent is responsible for connecting to a remote Doroseek instance (acting as a relay), managing the tunnel, and forwarding traffic between the relay and the local services.
Registration Process (Agent to Relay):
-
Connection: The local agent establishes a WebSocket connection to the
DoroseekMCP server:wss://your-doroseek-instance/mcp/tunnel/register?apiKey=<your_api_key>An API key is mandatory for registration.
-
Registration Message: After the WebSocket connection is established, the agent sends a
registermessage. This message details the services it wishes to expose. Exampleregistermessage from agent:{ "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. ] } } -
Server Response: Upon successful registration,
Doroseekresponds with aregisteredmessage containing the uniquetunnelIdand the public base URL for accessing the tunneled services. Exampleregisteredmessage from server:{ "type": "registered", "data": { "tunnelId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", "public_base_url": "https://your-doroseek-instance/t/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" } }
Accessing Tunneled Services:
Once the tunnel is established, your local services can be accessed via the public URLs derived from the public_base_url and the subdomain_or_path defined during registration. For example, if your-doroseek-instance is example.com and a service was registered with subdomain_or_path: 'my-web-app', it might be accessible via:
https://example.com/t/<tunnelId>/my-web-app/...
The exact URL structure depends on how the public-facing routing (/t/:tunnelId/...) is configured to map to specific services.
Local Agent Responsibilities (High-Level):
- Initiate and maintain the WebSocket connection to
/mcp/tunnel/register. - Send the
registermessage with service definitions. - Listen for
httpRequestmessages fromDoroseekover the WebSocket. EachhttpRequestmessage will contain:requestId: A unique ID for the request.data: An object withmethod,path,headers, andbodyof the incoming public request.
- Upon receiving an
httpRequest, the agent makes a corresponding request to its local service (e.g.,http://localhost:3000/some/path). - Once the local service responds, the agent sends an
httpResponsemessage back toDoroseekover the WebSocket, including the originalrequestIdand the response details (status, headers, body). - (WebSocket proxying through the tunnel is planned for future enhancements but is currently stubbed in
Doroseek's public-facing tunnel route).
2. Multi-User Communication Rooms
Concept: Enable real-time, bi-directional communication between multiple clients. This can be used as a backend for features like live chat in web applications, collaborative editing, or other multi-user interactive experiences.
Connecting to a Room:
Clients connect to a room using a WebSocket connection. The roomId is specified in the path.
- WebSocket Endpoint:
wss://your-doroseek-instance/mcp/room/:roomId?apiKey=<optional_api_key> - Authentication:
- Anonymous Access: Currently, clients can connect without an API key.
- API Key Authenticated Access: If an
apiKeyis provided in the query string, it is validated. This allows associating users with an API key owner if needed.
Core Functionality:
- Joining a Room: A user joins a room by successfully establishing a WebSocket connection to the room's endpoint.
- On join, the server sends a
ServerRoomInfoMessageto the joining user, containing a list of users already in the room. - Other users in the room receive a
ServerUserJoinedMessage.
- On join, the server sends a
- Leaving a Room: When a user's WebSocket connection is closed, they are automatically removed from the room.
- Other users in the room receive a
ServerUserLeftMessage.
- Other users in the room receive a
- Sending Chat Messages: Clients send chat messages over the WebSocket.
Example
ClientChatMessagefrom client:{ "type": "chatMessage", "payload": { "roomId": "your-target-room-id", // Though roomId is in path, can be in payload "message": "Hello everyone!" } } - Receiving Messages: Clients listen for messages from the server.
ServerChatMessage: A chat message sent by another user in the room.{ "type": "chatMessage", "payload": { "roomId": "the-room-id", "fromUserId": "user-id-of-sender", "message": "Hello everyone!", "timestamp": "2023-01-01T12:00:00.000Z" } }ServerUserJoinedMessage: Notifies that a new user has joined.ServerUserLeftMessage: Notifies that a user has left.
Conceptual Client-Side Interaction Example (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. Resource Sharing
Concept:
Allows users to upload small files or data snippets (e.g., configuration files, JSON data, small images) to Doroseek and share them via a unique, publicly accessible URL.
Upload Process (MCP WebSocket):
-
Endpoint: Connect via WebSocket to
/mcp/fileshare/upload.wss://your-doroseek-instance/mcp/fileshare/upload?apiKey=<your_api_key>A valid API key is required for uploading files.
-
Message Flow:
- Client -> Server:
ClientInitiateUploadMessageThe client first sends a message to initiate the upload, providing metadata about the file.{ "type": "initiateUpload", "payload": { "filename": "config.json", "filetype": "application/json", "size": 1024 // Size in bytes } } - Server -> Client:
ServerUploadReadyMessageIf the server accepts the upload (e.g., size is within limits), it responds with a uniqueresourceId.{ "type": "uploadReady", "payload": { "resourceId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" } } - Client -> Server:
ClientFileDataMessageThe client sends the actual file data, base64 encoded.{ "type": "fileData", "payload": { "resourceId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", "data": "eyJrZXkiOiAidmFsdWUifQ==" // Base64 encoded content of the file } } - Server -> Client:
ServerUploadCompleteMessageUpon successfully saving the file data, the server confirms completion and provides the download URL.{ "type": "uploadComplete", "payload": { "resourceId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", "downloadUrl": "https://your-doroseek-instance/shared/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" } }
- Client -> Server:
Download Process (HTTP GET):
- URL Format: Files can be downloaded via a simple HTTP GET request to:
https://your-doroseek-instance/shared/:resourceId - Authentication: By default, downloading does not require an API key. The link is publicly accessible if known.
Limitations:
- File Size Limit: Currently, this feature is intended for small files. Due to the use of Deno KV as a backend for storing file data directly, individual file sizes are effectively limited to Deno KV's value size limit, which is typically around 60KB. Uploads exceeding this will be rejected. Chunked uploading for larger files is a potential future enhancement.
Running locally
This section describes running Doroseek in its default Server Mode. For running Doroseek as a local agent for the Tunneling feature, see the Agent Mode section.
Server Mode Configuration
copy .env.example to .env
# .env
ADMIN_KEY=the_admin_key_here
MCP_MAX_DURATION=60
By default, the project allows any key to create its corresponding settings, if
you need to restrict access to only a specific key, you need to set the
ADMIN_KEY environment variable.
The MCP SSE connection can keep MCP_MAX_DURATION second.
Set the db if needed
// services/database.ts
export const db = await Deno.openKv("./db");
Running in Server Mode
To run the app in its default server mode, ensure DOROSEEK_AGENT_MODE_ENABLED is not set to "true".
You will need to install Deno. Then run from the root of this repository:
deno task start
Agent Mode (for Intranet Penetration)
Doroseek can also operate in Agent Mode. In this mode, it does not start the Fresh web server. Instead, it acts as a local client agent that connects to a remote Doroseek instance (acting as a relay server) to expose services from your private network to the internet.
This is an alternative running mode to its default server mode. When Agent Mode is enabled and configured correctly, Doroseek will exclusively perform agent duties.
Agent Mode Configuration
To enable and configure Agent Mode, you need to set the following environment variables:
-
DOROSEEK_AGENT_MODE_ENABLED:- Set to
"true"to enable Agent Mode. If this is not set to"true",Doroseekwill attempt to start in Server Mode.
- Set to
-
DOROSEEK_RELAY_URL:- The WebSocket URL of the remote
Doroseekinstance (acting as the relay server) to which this agent should connect. This URL should point to the relay's tunnel registration endpoint. - Example:
wss://your-remote-doroseek.com/mcp/tunnel/register
- The WebSocket URL of the remote
-
DOROSEEK_AGENT_API_KEY:- The API key that this agent will use to authenticate with the remote
Doroseekrelay server. This API key must be recognized and authorized by the relay server.
- The API key that this agent will use to authenticate with the remote
-
DOROSEEK_AGENT_SERVICES_JSON:-
A JSON string defining an array of local services that this agent should expose through the tunnel.
-
Each service object in the array must have the following fields:
id(string): A unique identifier for the service (chosen by you, e.g., "my-web").name(string): A user-friendly name for the service (e.g., "My Local Web Server").type(string): The type of service. Currently, only"http"is supported by the agent's HTTP handler.local_host(string): The hostname or IP address of the local service (e.g.,"localhost","127.0.0.1").local_port(number): The port number on which the local service is running (e.g.,8080).subdomainOrPath(string): A string that will be used by the relay server to form the public URL for this service. It should not contain/or spaces. It effectively becomes a path segment on the relay's public tunnel URL.- For example, if the relay provides a base URL like
https://<relay-public-domain>/t/<tunnelId>, and you setsubdomainOrPathto"myweb", your service will be accessible athttps://<relay-public-domain>/t/<tunnelId>/myweb/....
- For example, if the relay provides a base URL like
-
Example
DOROSEEK_AGENT_SERVICES_JSONvalue:[ { "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" } ]To set this as an environment variable, the JSON string typically needs to be compact (no newlines) and properly escaped if set in certain shell environments. For example, in a
.envfile or 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"}]'
-
Running in Agent Mode
- Set all the required environment variables listed above. Ensure
DOROSEEK_AGENT_MODE_ENABLEDis set to"true". - Run the application using the standard command:
deno task start - If the configuration is valid,
Doroseekwill start in Agent Mode. You will not see the usual Fresh server startup logs (e.g., "Listening on http://localhost:8000/"). - Instead, look for log messages indicating Agent Mode operation, such as:
[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
Interaction with the Relay (Agent Perspective)
- The agent (this
Doroseekinstance running in Agent Mode) establishes a WebSocket connection to theDOROSEEK_RELAY_URL, which should be the/mcp/tunnel/registerendpoint of a remoteDoroseekinstance running in Server Mode. - It sends a registration message detailing the local services defined in
DOROSEEK_AGENT_SERVICES_JSON. - The remote relay server, upon successful registration, provides the agent with a unique
tunnelIdand apublic_base_url. - Subsequently, when the relay server receives public HTTP requests matching the tunnel, it forwards these requests (as
httpRequestmessages) to the agent over the established WebSocket tunnel. - The agent processes these
httpRequestmessages, makes requests to the configured local services, and sendshttpResponsemessages back to the relay. - The agent also responds to
pingmessages from the relay withpongmessages, indicating its status and the health of its primary local service.
Tunnel Health Check API
To monitor the status of an established tunnel and the underlying local service, Doroseek provides a Health Check API endpoint.
-
Endpoint:
GET /tunnel/:tunnelId/status -
Description: This endpoint allows you to query the current status of a specific tunnel identified by
:tunnelId. It checks both the WebSocket connection from the relay to the local agent and the agent's ability to reach its configured local service(s). -
Authentication: This endpoint does not currently require specific API key authentication, but the
tunnelIdmust be valid and known to the system. Access control can be layered on top via standard reverse proxy or gateway mechanisms if needed. -
Response Body (
HealthStatusReport): The API returns a JSON object with the following structure:{ "tunnelId": "string", // The ID of the tunnel checked "tunnelStatus": "connected" | "disconnected" | "unknown", "localServiceStatus": "ok" | "error" | "unconfigured" | "timeout" | "unknown" | "agent_unresponsive", "checkedByInstanceId": "string", // ID of the relay instance that performed/reported the check "timestamp": "string" // ISO 8601 timestamp of when the check was performed }tunnelId: The ID of the tunnel that was checked.tunnelStatus:"connected": The relay server has an active WebSocket connection to the local agent for this tunnel."disconnected": The relay server does not have an active WebSocket connection to the local agent."unknown": The status of the tunnel could not be determined (e.g., tunnel ID not found in the primary database).
localServiceStatus:"ok": The local agent responded to a health check ping and reported its primary configured local service is reachable."error": The local agent responded, but reported an error when trying to check its local service."unconfigured": The local agent responded, but has no local services configured to check, or the service type is not checkable by the agent."timeout": The local agent responded, but its attempt to check the local service timed out."agent_unresponsive": The relay server is connected to the agent, but the agent did not respond to a health check ping within the expected time."unknown": The status of the local service could not be determined (e.g., becausetunnelStatusis not"connected").
checkedByInstanceId: The unique ID of the relay server instance that performed or last reported the health status. Useful in multi-instance deployments.timestamp: The ISO 8601 timestamp of when the health status was determined.
-
Example Usage: Request:
GET /tunnel/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/statusExample Response:
{ "tunnelId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", "tunnelStatus": "connected", "localServiceStatus": "ok", "checkedByInstanceId": "yyyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy", "timestamp": "2023-10-27T10:30:00.000Z" }Another Example (Agent connected, but local service is down):
{ "tunnelId": "zzzzzzzz-zzzz-zzzz-zzzz-zzzzzzzzzzzz", "tunnelStatus": "connected", "localServiceStatus": "error", "checkedByInstanceId": "wwwwwww-wwww-wwww-wwww-wwwwwwwwwwww", "timestamp": "2023-10-27T10:35:00.000Z" }