Compare commits

..

2 Commits

Author SHA1 Message Date
Danijel
87d83ab4c6 fix: store session after handleRequest so sessionId is initialized
transport.sessionId is only populated during handleRequest(), not
before. Reading it before that call stored sessions under key
`undefined`, causing all subsequent requests to get "Session not found."

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 02:17:56 +01:00
Danijel
2652f85bcf fix: remove docker-compose and update deployment docs
Remove docker-compose.yml since Dokploy deploys as a Swarm service
using the Dockerfile directly. Update README with Docker and Dokploy
deployment instructions. Fix default port to 3045 in http-server.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-18 01:28:52 +01:00
3 changed files with 20 additions and 34 deletions

View File

@@ -20,7 +20,7 @@ Cloud-hosted MCP server for [SolidTime](https://www.solidtime.io/) — the open-
- **Timezone support** — display and accept times in your local timezone
- **Built-in MCP instructions** — the server provides contextual guidance to AI clients for optimal tool usage
- **Session management** — per-user sessions with automatic 30-minute expiry
- **Docker-ready** — multi-stage Dockerfile and docker-compose included
- **Docker-ready** — multi-stage Dockerfile for easy deployment
- **Actionable error messages** — every error tells you what to do next
- **Zero external dependencies** beyond the MCP SDK (uses native `fetch`)
- Works with self-hosted SolidTime instances and the hosted version
@@ -93,26 +93,33 @@ Get your API token from **SolidTime > Settings > API**.
## Server Deployment
### Docker Compose (recommended)
```bash
docker compose up -d
```
### Docker Manual
### Docker
```bash
docker build -t solidtime-mcp-server .
docker run -p 3000:3000 solidtime-mcp-server
docker run -d \
-e PORT=3045 \
-e SOLIDTIME_API_URL=https://app.solidtime.io \
-p 3045:3045 \
solidtime-mcp-server
```
### Dokploy / Docker Swarm
Create an **Application** (not Compose) in Dokploy pointing to your Git repo. Dokploy will build using the Dockerfile and deploy as a Swarm service. Configure in the Dokploy dashboard:
- **Domain**: your desired hostname, pointing to port `3045`
- **Environment variables**: `PORT=3045` and `SOLIDTIME_API_URL`
> **Note**: Compose-type deployments use `docker compose up` which creates plain containers. If your Dokploy instance runs Traefik with Docker Swarm, use Application type so Traefik's Swarm provider can discover the service.
### Server Environment Variables
These are set on the server, not by MCP clients:
| Variable | Required | Default | Description |
|----------|----------|---------|-------------|
| `PORT` | No | `3000` | HTTP server port |
| `PORT` | No | `3045` | HTTP server port |
| `SOLIDTIME_API_URL` | No | `https://app.solidtime.io` | Default SolidTime API URL (clients can override via header) |
### Endpoints

View File

@@ -1,21 +0,0 @@
services:
solidtime-mcp:
build: .
environment:
- PORT=3045
# Optional: default SolidTime API URL for all sessions.
# Clients can override this via the x-solidtime-api-url header.
- SOLIDTIME_API_URL=${SOLIDTIME_API_URL:-https://app.solidtime.io}
restart: unless-stopped
networks:
- dokploy-network
healthcheck:
test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:3045/health"]
interval: 30s
timeout: 5s
retries: 3
start_period: 10s
networks:
dokploy-network:
external: true

View File

@@ -4,7 +4,7 @@ import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/
import { isInitializeRequest } from "@modelcontextprotocol/sdk/types.js";
import { createServer } from "./server.js";
const PORT = parseInt(process.env.PORT || "3000", 10);
const PORT = parseInt(process.env.PORT || "3045", 10);
const DEFAULT_SOLIDTIME_API_URL = process.env.SOLIDTIME_API_URL;
interface Session {
@@ -134,14 +134,14 @@ const httpServer = http.createServer(async (req, res) => {
const mcpServer = await createServer(config);
await mcpServer.connect(transport);
await transport.handleRequest(req, res, parsedBody);
const sid = transport.sessionId!;
sessions.set(sid, { transport, createdAt: Date.now() });
transport.onclose = () => {
sessions.delete(sid);
};
await transport.handleRequest(req, res, parsedBody);
} else {
// Existing session
const session = sessions.get(sessionId);