x
This commit is contained in:
@@ -27,6 +27,9 @@ RUN apk add --update --no-cache catatonit
|
|||||||
# Final stage with explicit platform specification
|
# Final stage with explicit platform specification
|
||||||
FROM python:3.13-alpine
|
FROM python:3.13-alpine
|
||||||
|
|
||||||
|
# Install Node.js and npm
|
||||||
|
RUN apk add --no-cache nodejs npm
|
||||||
|
|
||||||
COPY --from=uv --chown=app:app /app/.venv /app/.venv
|
COPY --from=uv --chown=app:app /app/.venv /app/.venv
|
||||||
COPY --from=uv /usr/bin/catatonit /usr/bin/
|
COPY --from=uv /usr/bin/catatonit /usr/bin/
|
||||||
COPY --from=uv /usr/libexec/podman/catatonit /usr/libexec/podman/
|
COPY --from=uv /usr/libexec/podman/catatonit /usr/libexec/podman/
|
||||||
|
|||||||
50
build-and-push.sh
Executable file
50
build-and-push.sh
Executable file
@@ -0,0 +1,50 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
set -e
|
||||||
|
|
||||||
|
REGISTRY="git.baked.rocks"
|
||||||
|
OWNER="vasceannie"
|
||||||
|
# Extract service names from docker-compose.yml
|
||||||
|
SERVICES=($(docker compose config --services))
|
||||||
|
|
||||||
|
# Login to registry if not already authenticated
|
||||||
|
echo "Logging in to ${REGISTRY}..."
|
||||||
|
docker login "${REGISTRY}" || {
|
||||||
|
echo "Error: Failed to login to ${REGISTRY}"
|
||||||
|
echo "Please ensure you have valid credentials"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# Build all services
|
||||||
|
echo "Building all services..."
|
||||||
|
docker compose build
|
||||||
|
|
||||||
|
# Tag and push each service
|
||||||
|
for service in "${SERVICES[@]}"; do
|
||||||
|
echo "Tagging and pushing $service..."
|
||||||
|
# Get the image ID - try multiple methods
|
||||||
|
IMAGE_ID=$(docker compose images -q "$service" 2>/dev/null | head -1)
|
||||||
|
|
||||||
|
if [ -z "$IMAGE_ID" ]; then
|
||||||
|
# Fallback: find image by service name pattern
|
||||||
|
IMAGE_ID=$(docker images --format "{{.ID}}" --filter "reference=*${service}*" | head -1)
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "$IMAGE_ID" ]; then
|
||||||
|
echo "Error: Could not find image ID for $service"
|
||||||
|
echo "Available images:"
|
||||||
|
docker compose images
|
||||||
|
docker images | grep -E "(${SERVICES[*]// /|})"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
TARGET_IMAGE="${OWNER}/${service}:latest"
|
||||||
|
REGISTRY_IMAGE="${REGISTRY}/${TARGET_IMAGE}"
|
||||||
|
|
||||||
|
# Tag using image ID directly
|
||||||
|
docker tag "${IMAGE_ID}" "${REGISTRY_IMAGE}"
|
||||||
|
docker push "${REGISTRY_IMAGE}"
|
||||||
|
echo "$service pushed successfully"
|
||||||
|
done
|
||||||
|
|
||||||
|
echo "All services built and pushed to ${REGISTRY}"
|
||||||
|
|
||||||
56
config.json
Normal file
56
config.json
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
{
|
||||||
|
"mcpServers": {
|
||||||
|
"pieces": {
|
||||||
|
"enabled": true,
|
||||||
|
"timeout": 60,
|
||||||
|
"url": "http://localhost:39300/model_context_protocol/2024-11-05/sse",
|
||||||
|
"transportType": "sse",
|
||||||
|
"command": "echo",
|
||||||
|
"args": ["sse-placeholder"]
|
||||||
|
},
|
||||||
|
"firecrawl": {
|
||||||
|
"enabled": true,
|
||||||
|
"timeout": 60,
|
||||||
|
"command": "npx",
|
||||||
|
"args": [
|
||||||
|
"-y",
|
||||||
|
"firecrawl-mcp"
|
||||||
|
],
|
||||||
|
"transportType": "stdio",
|
||||||
|
"env": {
|
||||||
|
"FIRECRAWL_API_URL": "http://crawl.lab:30002",
|
||||||
|
"FIRECRAWL_API_KEY": "dummy-key"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"context7": {
|
||||||
|
"enabled": true,
|
||||||
|
"timeout": 60,
|
||||||
|
"url": "https://mcp.context7.com/mcp",
|
||||||
|
"transportType": "http",
|
||||||
|
"command": "echo",
|
||||||
|
"args": ["http-placeholder"],
|
||||||
|
"headers": {
|
||||||
|
"CONTEXT7_API_KEY": "ctx7sk-f6f1b998-88a2-4e78-9d21-433545326e6c"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"knowledge-graph": {
|
||||||
|
"enabled": true,
|
||||||
|
"timeout": 60,
|
||||||
|
"url": "http://localhost:48000/sse",
|
||||||
|
"transportType": "sse",
|
||||||
|
"command": "echo",
|
||||||
|
"args": ["sse-placeholder"]
|
||||||
|
},
|
||||||
|
"xpipe": {
|
||||||
|
"enabled": true,
|
||||||
|
"timeout": 60,
|
||||||
|
"url": "http://localhost:21721/mcp",
|
||||||
|
"transportType": "http",
|
||||||
|
"command": "echo",
|
||||||
|
"args": ["http-placeholder"],
|
||||||
|
"headers": {
|
||||||
|
"Authorization": "Bearer 27d1c4ca-dfec-4d38-adc1-1e0f4bcbaadc"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
{
|
|
||||||
"mcpServers": {
|
|
||||||
"fetch": {
|
|
||||||
"enabled": true,
|
|
||||||
"timeout": 60,
|
|
||||||
"command": "uvx",
|
|
||||||
"args": [
|
|
||||||
"mcp-server-fetch"
|
|
||||||
],
|
|
||||||
"transportType": "stdio"
|
|
||||||
},
|
|
||||||
"github": {
|
|
||||||
"enabled": false,
|
|
||||||
"timeout": 60,
|
|
||||||
"command": "npx",
|
|
||||||
"args": [
|
|
||||||
"-y",
|
|
||||||
"@modelcontextprotocol/server-github"
|
|
||||||
],
|
|
||||||
"transportType": "stdio"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
147
docker-compose.yml
Normal file
147
docker-compose.yml
Normal file
@@ -0,0 +1,147 @@
|
|||||||
|
version: '3.8'
|
||||||
|
|
||||||
|
networks:
|
||||||
|
traefik:
|
||||||
|
external: true
|
||||||
|
|
||||||
|
services:
|
||||||
|
pieces-mcp:
|
||||||
|
image: git.baked.rocks/vasceannie/pieces-mcp:latest
|
||||||
|
deploy:
|
||||||
|
placement:
|
||||||
|
constraints:
|
||||||
|
- node.hostname == wslbox
|
||||||
|
labels:
|
||||||
|
- "traefik.enable=true"
|
||||||
|
- "traefik.http.routers.pieces-mcp.rule=Host(`mcp.box`) && PathPrefix(`/pieces-mcp`)"
|
||||||
|
- "traefik.http.routers.pieces-mcp.entrypoints=web"
|
||||||
|
- "traefik.http.services.pieces-mcp.loadbalancer.server.port=8096"
|
||||||
|
- "traefik.http.middlewares.pieces-mcp-stripprefix.stripprefix.prefixes=/pieces-mcp"
|
||||||
|
- "traefik.http.routers.pieces-mcp.middlewares=pieces-mcp-stripprefix"
|
||||||
|
restart_policy:
|
||||||
|
condition: on-failure
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD", "sh", "-c", "netstat -tln | grep ':8096 ' || exit 1"]
|
||||||
|
interval: 30s
|
||||||
|
timeout: 10s
|
||||||
|
retries: 3
|
||||||
|
start_period: 40s
|
||||||
|
command: >
|
||||||
|
--named-server pieces-os 'mcp-proxy --transport sse http://host.docker.internal:39300/model_context_protocol/2024-11-05/sse'
|
||||||
|
--port=8096 --host=0.0.0.0 --allow-origin='*'
|
||||||
|
networks:
|
||||||
|
- traefik
|
||||||
|
|
||||||
|
firecrawl-mcp:
|
||||||
|
image: git.baked.rocks/vasceannie/firecrawl-mcp:latest
|
||||||
|
deploy:
|
||||||
|
placement:
|
||||||
|
constraints:
|
||||||
|
- node.hostname == wslbox
|
||||||
|
labels:
|
||||||
|
- "traefik.enable=true"
|
||||||
|
- "traefik.http.routers.firecrawl-mcp.rule=Host(`mcp.box`) && PathPrefix(`/firecrawl-mcp`)"
|
||||||
|
- "traefik.http.routers.firecrawl-mcp.entrypoints=web"
|
||||||
|
- "traefik.http.services.firecrawl-mcp.loadbalancer.server.port=8097"
|
||||||
|
- "traefik.http.middlewares.firecrawl-mcp-stripprefix.stripprefix.prefixes=/firecrawl-mcp"
|
||||||
|
- "traefik.http.routers.firecrawl-mcp.middlewares=firecrawl-mcp-stripprefix"
|
||||||
|
restart_policy:
|
||||||
|
condition: on-failure
|
||||||
|
environment:
|
||||||
|
- FIRECRAWL_API_URL=http://crawl.toy
|
||||||
|
- FIRECRAWL_API_KEY=dummy-key
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD", "sh", "-c", "netstat -tln | grep ':809[6-9]\\|:8100' || exit 1"]
|
||||||
|
interval: 30s
|
||||||
|
timeout: 10s
|
||||||
|
retries: 3
|
||||||
|
start_period: 40s
|
||||||
|
command: >
|
||||||
|
--pass-environment
|
||||||
|
--stateless
|
||||||
|
--transport streamablehttp
|
||||||
|
--port=8097 --host=0.0.0.0
|
||||||
|
npx firecrawl-mcp
|
||||||
|
networks:
|
||||||
|
- traefik
|
||||||
|
|
||||||
|
context7-mcp:
|
||||||
|
image: git.baked.rocks/vasceannie/context7-mcp:latest
|
||||||
|
deploy:
|
||||||
|
placement:
|
||||||
|
constraints:
|
||||||
|
- node.hostname == wslbox
|
||||||
|
labels:
|
||||||
|
- "traefik.enable=true"
|
||||||
|
- "traefik.http.routers.context7-mcp.rule=Host(`mcp.box`) && PathPrefix(`/context7-mcp`)"
|
||||||
|
- "traefik.http.routers.context7-mcp.entrypoints=web"
|
||||||
|
- "traefik.http.services.context7-mcp.loadbalancer.server.port=8098"
|
||||||
|
- "traefik.http.middlewares.context7-mcp-stripprefix.stripprefix.prefixes=/context7-mcp"
|
||||||
|
- "traefik.http.routers.context7-mcp.middlewares=context7-mcp-stripprefix"
|
||||||
|
restart_policy:
|
||||||
|
condition: on-failure
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD", "sh", "-c", "netstat -tln | grep ':809[6-9]\\|:8100' || exit 1"]
|
||||||
|
interval: 30s
|
||||||
|
timeout: 10s
|
||||||
|
retries: 3
|
||||||
|
start_period: 40s
|
||||||
|
command: >
|
||||||
|
--named-server context7 'mcp-proxy --stateless --transport streamablehttp --headers CONTEXT7_API_KEY ctx7sk-f6f1b998-88a2-4e78-9d21-433545326e6c https://mcp.context7.com/mcp'
|
||||||
|
--port=8098 --host=0.0.0.0 --allow-origin='*'
|
||||||
|
networks:
|
||||||
|
- traefik
|
||||||
|
|
||||||
|
# knowledge-graph-mcp:
|
||||||
|
# image: git.baked.rocks/vasceannie/knowledge-graph-mcp:latest
|
||||||
|
# deploy:
|
||||||
|
# placement:
|
||||||
|
# constraints:
|
||||||
|
# - node.hostname == wslbox
|
||||||
|
# labels:
|
||||||
|
# - "traefik.enable=true"
|
||||||
|
# - "traefik.http.routers.knowledge-graph-mcp.rule=Host(`mcp.box`) && PathPrefix(`/knowledge-graph-mcp`)"
|
||||||
|
# - "traefik.http.routers.knowledge-graph-mcp.entrypoints=web"
|
||||||
|
# - "traefik.http.services.knowledge-graph-mcp.loadbalancer.server.port=8099"
|
||||||
|
# - "traefik.http.middlewares.knowledge-graph-mcp-stripprefix.stripprefix.prefixes=/knowledge-graph-mcp"
|
||||||
|
# - "traefik.http.routers.knowledge-graph-mcp.middlewares=knowledge-graph-mcp-stripprefix"
|
||||||
|
# restart_policy:
|
||||||
|
# condition: on-failure
|
||||||
|
# healthcheck:
|
||||||
|
# test: ["CMD", "sh", "-c", "netstat -tln | grep ':809[6-9]\\|:8100' || exit 1"]
|
||||||
|
# interval: 30s
|
||||||
|
# timeout: 10s
|
||||||
|
# retries: 3
|
||||||
|
# start_period: 40s
|
||||||
|
# command: >
|
||||||
|
# --named-server knowledge-graph 'mcp-proxy --transport sse http://host.docker.internal:48000/sse'
|
||||||
|
# --port=8099 --host=0.0.0.0 --allow-origin='*'
|
||||||
|
# networks:
|
||||||
|
# - traefik
|
||||||
|
|
||||||
|
xpipe-mcp:
|
||||||
|
image: git.baked.rocks/vasceannie/xpipe-mcp:latest
|
||||||
|
deploy:
|
||||||
|
placement:
|
||||||
|
constraints:
|
||||||
|
- node.hostname == wslbox
|
||||||
|
labels:
|
||||||
|
- "traefik.enable=true"
|
||||||
|
- "traefik.http.routers.xpipe-mcp.rule=Host(`mcp.box`) && PathPrefix(`/xpipe-mcp`)"
|
||||||
|
- "traefik.http.routers.xpipe-mcp.entrypoints=web"
|
||||||
|
- "traefik.http.services.xpipe-mcp.loadbalancer.server.port=8100"
|
||||||
|
- "traefik.http.middlewares.xpipe-mcp-stripprefix.stripprefix.prefixes=/xpipe-mcp"
|
||||||
|
- "traefik.http.routers.xpipe-mcp.middlewares=xpipe-mcp-stripprefix"
|
||||||
|
restart_policy:
|
||||||
|
condition: on-failure
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD", "sh", "-c", "netstat -tln | grep ':809[6-9]\\|:8100' || exit 1"]
|
||||||
|
interval: 30s
|
||||||
|
timeout: 10s
|
||||||
|
retries: 3
|
||||||
|
start_period: 40s
|
||||||
|
command: >
|
||||||
|
--named-server xpipe 'mcp-proxy --transport streamablehttp --headers Authorization "Bearer 27d1c4ca-dfec-4d38-adc1-1e0f4bcbaadc" http://host.docker.internal:21721/mcp'
|
||||||
|
--port=8100 --host=0.0.0.0 --allow-origin='*'
|
||||||
|
networks:
|
||||||
|
- traefik
|
||||||
@@ -90,10 +90,28 @@ def create_single_instance_routes(
|
|||||||
_update_global_activity()
|
_update_global_activity()
|
||||||
await http_session_manager.handle_request(scope, receive, send)
|
await http_session_manager.handle_request(scope, receive, send)
|
||||||
|
|
||||||
|
async def handle_sse_messages(scope: Scope, receive: Receive, send: Send) -> None:
|
||||||
|
_update_global_activity()
|
||||||
|
try:
|
||||||
|
await sse_transport.handle_post_message(scope, receive, send)
|
||||||
|
except Exception as e:
|
||||||
|
# If session is not found or other SSE errors, return a proper HTTP response
|
||||||
|
if "Could not find session" in str(e) or "session" in str(e).lower():
|
||||||
|
logger.warning("Session not found for SSE message request, client should reconnect: %s", e)
|
||||||
|
response = JSONResponse(
|
||||||
|
{"error": "Session not found", "message": "Please reconnect to establish a new session"},
|
||||||
|
status_code=410, # 410 Gone - indicates resource is no longer available
|
||||||
|
)
|
||||||
|
await response(scope, receive, send)
|
||||||
|
else:
|
||||||
|
# Re-raise other exceptions
|
||||||
|
raise
|
||||||
|
|
||||||
routes = [
|
routes = [
|
||||||
Mount("/mcp", app=handle_streamable_http_instance),
|
Mount("/mcp", app=handle_streamable_http_instance),
|
||||||
Route("/sse", endpoint=handle_sse_instance),
|
Route("/sse", endpoint=handle_sse_instance),
|
||||||
Mount("/messages/", app=sse_transport.handle_post_message),
|
Mount("/messages/", app=handle_sse_messages),
|
||||||
|
Mount("/", app=handle_streamable_http_instance),
|
||||||
]
|
]
|
||||||
return routes, http_session_manager
|
return routes, http_session_manager
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user