Skip to content

sys___init requirement breaks standard MCP client compatibility #7

@Mossaka

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

Sub-issues

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