Merge remote-tracking branch 'gitea/mcps'
Some checks failed
CI / coverage (push) Has been cancelled
CI / check (push) Has been cancelled
CI / release (push) Has been cancelled
CI / lint (push) Has been cancelled
CI / mypy (push) Has been cancelled
CI / test on 3.10 (push) Has been cancelled
CI / test on 3.11 (push) Has been cancelled
CI / test on 3.12 (push) Has been cancelled
CI / test on 3.13 (push) Has been cancelled
CI / test on 3.14 (push) Has been cancelled
CI / test on 3.14t (push) Has been cancelled

This commit is contained in:
2025-11-26 08:33:03 -05:00
6 changed files with 275 additions and 24 deletions

View File

@@ -27,6 +27,9 @@ RUN apk add --update --no-cache catatonit
# Final stage with explicit platform specification
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 /usr/bin/catatonit /usr/bin/
COPY --from=uv /usr/libexec/podman/catatonit /usr/libexec/podman/

50
build-and-push.sh Executable file
View 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
View 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"
}
}
}
}

View File

@@ -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
View 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

View File

@@ -124,6 +124,23 @@ def create_single_instance_routes(
await http_session_manager.handle_request(updated_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 = [
Route(
"/mcp",
@@ -133,7 +150,8 @@ def create_single_instance_routes(
),
Mount("/mcp", app=handle_streamable_http_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