Skip to content

sys___init requirement breaks standard MCP client compatibility #7

Description

@Mossaka

Problem

When testing the MCP gateway locally, tool calls fail with the error:

sys___init must be called before any other tool calls

This happens because FlowGuard requires a sys___init call to create a session before any other tool can be invoked.

Environment Setup

Step 1: Build the gateway

cd gh-aw-mcpg
go mod tidy
go build -o flowguard-go

Step 2: Create config file (/tmp/mcpg-config.json)

{
  "mcpServers": {
    "github": {
      "type": "local",
      "container": "ghcr.io/github/github-mcp-server:v0.19.0",
      "env": {
        "GITHUB_PERSONAL_ACCESS_TOKEN": ""
      }
    }
  }
}

Note: The config requires "type": "local" and uses container field (not raw command/args).

Step 3: Run the gateway

export GITHUB_PERSONAL_ACCESS_TOKEN="<your-token>"
./flowguard-go --routed --listen 127.0.0.1:8000 --config-stdin < /tmp/mcpg-config.json

Server starts successfully with 46 GitHub tools registered:

Loaded 1 MCP server(s)
Registered 46 tools from github
Starting FlowGuard in ROUTED mode on 127.0.0.1:8000
Routes: /mcp/<server> where <server> is one of: [github]
Registered route: /mcp/sys
Registered route: /mcp/github

Step 4: Health check works

curl http://127.0.0.1:8000/health
# Returns: OK

Steps to Reproduce the Issue

Using MCP Inspector (FAILS)

npx @modelcontextprotocol/inspector --cli http://127.0.0.1:8000/mcp/github \
  --transport http \
  --method tools/call \
  --header "Authorization: Bearer test-token" \
  --tool-name list_issues \
  --tool-arg owner=containerd \
  --tool-arg repo=runwasi \
  --tool-arg state=OPEN

Result:

{
  "content": [{ "type": "text", "text": "sys___init must be called before any other tool calls" }],
  "isError": true
}

Listing tools works fine

npx @modelcontextprotocol/inspector --cli http://127.0.0.1:8000/mcp/github \
  --transport http \
  --method tools/list \
  --header "Authorization: Bearer test-token"

This returns all 46 tools successfully.

Root Cause

In internal/server/unified.go:510-525, the requireSession() function checks if a session exists:

func (us *UnifiedServer) requireSession(ctx context.Context) error {
    sessionID := us.getSessionID(ctx)
    us.sessionMu.RLock()
    session := us.sessions[sessionID]
    us.sessionMu.RUnlock()

    if session == nil {
        return fmt.Errorf("sys___init must be called before any other tool calls")
    }
    return nil
}

This session is only created when sys___init is called first.

Impact

Standard MCP clients (MCP Inspector, Claude Desktop, Copilot CLI, etc.) expect to:

  1. Initialize the MCP connection (via initialize JSON-RPC method)
  2. Immediately call tools

They don't expect a custom sys___init tool call as a prerequisite. This breaks compatibility with standard MCP tooling.

Workaround (curl with manual session handling)

This multi-step curl approach works:

MCP_SYS_URL="http://127.0.0.1:8000/mcp/sys"
MCP_GITHUB_URL="http://127.0.0.1:8000/mcp/github"
BEARER="my-session-token"

# Step 1: Initialize sys session
SYS_SESSION=$(curl -isS -X POST $MCP_SYS_URL \
  -H 'Content-Type: application/json' \
  -H 'Accept: application/json, text/event-stream' \
  -H "Authorization: Bearer $BEARER" \
  -d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"1.0.0","capabilities":{},"clientInfo":{"name":"curl","version":"0.1"}}}' \
  | awk 'BEGIN{IGNORECASE=1} /^mcp-session-id:/{print $2}' | tr -d '\r')

# Step 2: Call sys___init to create session
curl -s \
  -H "Content-Type: application/json" \
  -H "Mcp-Session-Id: $SYS_SESSION" \
  -H "Authorization: Bearer $BEARER" \
  -X POST $MCP_SYS_URL \
  -d '{"jsonrpc": "2.0", "id": 2, "method": "tools/call", "params": {"name": "init", "arguments": {}}}'

# Step 3: Initialize github session
GH_SESSION=$(curl -isS -X POST $MCP_GITHUB_URL \
  -H 'Content-Type: application/json' \
  -H 'Accept: application/json, text/event-stream' \
  -H "Authorization: Bearer $BEARER" \
  -d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"1.0.0","capabilities":{},"clientInfo":{"name":"curl","version":"0.1"}}}' \
  | awk 'BEGIN{IGNORECASE=1} /^mcp-session-id:/{print $2}' | tr -d '\r')

# Step 4: Now tool calls work!
curl -s \
  -H "Content-Type: application/json" \
  -H "Mcp-Session-Id: $GH_SESSION" \
  -H "Authorization: Bearer $BEARER" \
  -X POST $MCP_GITHUB_URL \
  -d '{
    "jsonrpc": "2.0",
    "id": 3,
    "method": "tools/call",
    "params": {
      "name": "list_issues",
      "arguments": {
        "owner": "containerd",
        "repo": "runwasi",
        "state": "OPEN",
        "perPage": 5
      }
    }
  }'

Note: The state parameter must be uppercase (OPEN not open).

Suggested Solutions

  1. Remove the requirement - Remove requireSession() checks from tool handlers
  2. Auto-create sessions - Create a session automatically on first tool call if none exists
  3. Make it configurable - Add a flag like --require-init to enable/disable this behavior (default off)

Additional Notes

  • The MCP Inspector creates a new session per invocation, so it can't maintain session state across calls
  • The sys___init design was intended for DIFC (Decentralized Information Flow Control) per-agent tracking
  • If DIFC tracking is not needed, the session requirement should be optional

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions