Skip to main content

Overview

Guardway Gateway provides comprehensive Model Context Protocol (MCP) integration, enabling LLMs to use external tools and services through a unified interface. MCP servers can be run locally (via stdio) or accessed remotely (via HTTP), with full access control and monitoring.

Local & Remote Servers

Support for both stdio and HTTP transports

Automatic Discovery

Tool and resource discovery

Access Control

Per-API-key tool filtering (allowlist/denylist)

OAuth Support

Authentication for remote servers

Session Management

Persistent MCP sessions

Tool Filtering

Fine-grained access control

Metrics & Monitoring

Comprehensive observability

Multiple Transports

stdio (Python/Node.js) and HTTP

What is MCP (Model Context Protocol)

Model Context Protocol is a standard protocol that allows LLMs to interact with external services, tools, and data sources in a consistent way.

Core Concepts

Tools

Functions that LLMs can call. Defined with JSON Schema for parameters. Examples: file operations, web searches, API calls.

Resources

Data sources that LLMs can query. URI-based access. Examples: documents, databases, files.

Prompts

Pre-defined prompt templates. Reusable conversation starters. Examples: code review, summarization.

Transports

stdio: Bidirectional JSON-RPC over stdin/stdout. HTTP: RESTful JSON-RPC over HTTP.

Protocol Flow

Client (Guardway Gateway)                MCP Server
     |                             |
     |--- initialize ------------->|
     |<-- initialized -------------|
     |                             |
     |--- tools/list ------------->|
     |<-- tools list --------------|
     |                             |
     |--- tools/call ------------->|
     |    {name, arguments}        |
     |<-- result ------------------|

MCP Architecture in Guardway Gateway

Components

Location: /apps/gateway/src/mcp/
mcp/
├── manager.ts        # MCP server lifecycle management
└── filter-tools.ts   # Tool access control

MCP Manager

The McpManager class handles:
  • Server process spawning (local servers)
  • Transport detection (stdio vs HTTP)
  • JSON-RPC message proxying
  • Session lifecycle
  • Health monitoring
export class McpManager {
  async ensureLocalStarted(name: string, cfg: McpServerConfig): Promise<RuntimeEntry>
  async sendStdio(name: string, cfg: McpServerConfig, body: string): Promise<Response>
  async stop(name: string): Promise<void>
  async upstreamUrl(name: string, cfg: McpServerConfig): Promise<string>
  async status(name: string, cfg?: McpServerConfig): Promise<StatusInfo>
  async applyConfig(name: string, cfg: McpServerConfig): Promise<void>
}

Data Flow

User Request

[Chat Completion API]
    ├─ Parse tools parameter
    ├─ Discover available tools
    └─ Filter tools by API key policy

LLM (with tools)
    ├─ Decides to use tool
    └─ Returns tool_calls

[MCP Proxy]
    ├─ Get server runtime
    ├─ Proxy tools/call to MCP server
    └─ Return result

[Tool Response]

LLM (with tool result)

Final Response

Transport Detection

Guardway Gateway automatically detects the appropriate transport:
  • Python MCP servers (mcp-server-*)
  • JavaScript MCP servers (@modelcontextprotocol/server-*)
  • Commands containing uvx, python, pipx

MCP Services

Guardway Gateway includes built-in MCP servers and supports adding custom servers.

Built-in MCP Servers

1. Filesystem Server Access local filesystem with safety constraints:
{
  "name": "filesystem",
  "mode": "local",
  "enabled": true,
  "local": {
    "command": "npx",
    "args": ["@modelcontextprotocol/server-filesystem", "/app/data"]
  },
  "allowedTools": ["read_file", "list_directory"]
}
Tools:
  • read_file - Read file contents
  • write_file - Write to file
  • list_directory - List directory contents
  • search_files - Search for files
2. Git Server Git operations and repository access:
{
  "name": "git",
  "mode": "local",
  "enabled": true,
  "local": {
    "command": "npx",
    "args": ["@modelcontextprotocol/server-git"]
  }
}
Tools:
  • git_status - Get repository status
  • git_diff - Show differences
  • git_log - View commit history
  • git_show - Show commit details
3. Time Server Date and time operations:
{
  "name": "time",
  "mode": "local",
  "enabled": true,
  "local": {
    "command": "npx",
    "args": ["@modelcontextprotocol/server-time"]
  }
}
Tools:
  • get_current_time - Get current time
  • get_timezone - Get timezone info
  • convert_time - Convert between timezones
4. Fetch Server HTTP requests and web scraping:
{
  "name": "fetch",
  "mode": "local",
  "enabled": true,
  "local": {
    "command": "npx",
    "args": ["@modelcontextprotocol/server-fetch"]
  }
}
Tools:
  • fetch - Make HTTP requests
  • fetch_html - Fetch and parse HTML
  • fetch_json - Fetch JSON data

Python MCP Servers

Brave Search:
{
  "name": "brave-search",
  "mode": "local",
  "enabled": true,
  "local": {
    "command": "uvx",
    "args": ["mcp-server-brave-search"],
    "env": {
      "BRAVE_API_KEY": "your-api-key"
    }
  }
}
PostgreSQL:
{
  "name": "postgres",
  "mode": "local",
  "enabled": true,
  "local": {
    "command": "uvx",
    "args": ["mcp-server-postgres"],
    "env": {
      "POSTGRES_CONNECTION": "postgresql://user:pass@localhost/db"
    }
  }
}
Playwright (Browser Automation):
{
  "name": "playwright",
  "mode": "local",
  "enabled": true,
  "local": {
    "command": "npx",
    "args": ["@playwright/mcp"]
  }
}

Adding MCP Servers

Via Admin UI

1

Navigate to MCP page

2

Click Add MCP Server

3

Fill in configuration

  • Name: Unique identifier
  • Mode: Local or Remote
  • Display Name: Human-readable name
  • Description: What the server does
  • Command/URL: Execution details
  • Allowed Tools: Tool filtering (optional)
4

Click Save

5

Monitor status in server list

Via API

POST /v1/mcp/servers
Content-Type: application/json
Authorization: Bearer YOUR_ADMIN_KEY

{
  "name": "my-server",
  "mode": "local",
  "enabled": true,
  "displayName": "My Custom Server",
  "description": "Custom MCP server for XYZ",
  "local": {
    "command": "python",
    "args": ["-m", "my_mcp_server"],
    "env": {
      "API_KEY": "secret-key"
    }
  },
  "allowedTools": ["tool1", "tool2"]
}

Local Server Configuration

Command Options:
{
  "local": {
    "command": "npx",           // Executable command
    "args": [                   // Command arguments
      "@modelcontextprotocol/server-filesystem",
      "/app/data"
    ],
    "env": {                    // Environment variables
      "LOG_LEVEL": "info",
      "DATA_PATH": "/app/data"
    }
  }
}
Common Commands:
{
  "command": "npx",
  "args": ["@modelcontextprotocol/server-name"]
}

Remote Server Configuration

HTTP Servers:
{
  "mode": "remote",
  "remote": {
    "baseUrl": "https://mcp.example.com/api/v1",
    "headers": {
      "Authorization": "Bearer token",
      "X-Custom-Header": "value"
    }
  }
}
With OAuth:
{
  "mode": "remote",
  "remote": {
    "baseUrl": "https://mcp.example.com/api/v1",
    "oauth": {
      "clientId": "client-id",
      "clientSecret": "client-secret",
      "tokenUrl": "https://auth.example.com/oauth/token",
      "scopes": "mcp.read mcp.write",
      "grantType": "client_credentials"
    }
  }
}

OAuth Configuration for Remote Servers

Store OAuth client secrets securely. Never embed them directly in configuration files checked into version control. Use environment variables or a secrets manager to inject credentials at runtime.

Grant Types

The most common grant type for server-to-server communication:
{
  "oauth": {
    "clientId": "your-client-id",
    "clientSecret": "your-client-secret",
    "tokenUrl": "https://auth.provider.com/oauth/token",
    "grantType": "client_credentials",
    "scopes": "mcp.read mcp.write"
  }
}
Token Request:
POST https://auth.provider.com/oauth/token
Content-Type: application/x-www-form-urlencoded

grant_type=client_credentials
&client_id=your-client-id
&client_secret=your-client-secret
&scope=mcp.read+mcp.write

Token Management

Automatic Refresh:
  • Guardway Gateway automatically requests tokens before expiration
  • 5-minute buffer for token refresh
  • Cached tokens reused until expiration
Storage:
  • Tokens stored in memory (not persisted)
  • Cleared on server restart
  • Automatically refreshed on first use
Manual Refresh:
DELETE /v1/mcp/servers/my-server/token
Authorization: Bearer YOUR_ADMIN_KEY

Tool Filtering

Global Tool Allowlist

Restrict which tools a server can expose:
{
  "name": "filesystem",
  "allowedTools": [
    "read_file",
    "list_directory"
  ]
}
Only read_file and list_directory are available when using a global allowlist, even if the server offers more tools. This is useful for restricting a server to a safe subset of its capabilities.

Per-API-Key Tool Policies

Fine-grained access control per API key: Location: API Key configuration
{
  "id": "key-123",
  "name": "Production API Key",
  "mcpPolicy": {
    "access": "restricted",
    "servers": {
      "allow": ["filesystem", "time"],
      "deny": ["git", "fetch"]
    },
    "tools": {
      "filesystem": {
        "allow": ["read_file", "list_directory"],
        "deny": ["write_file", "delete_file"]
      },
      "time": {
        "allow": ["get_current_time"]
      },
      "*": {
        "deny": ["dangerous_operation"]
      }
    }
  }
}
Policy Structure:
type KeyMcpPolicy = {
  access?: 'all' | 'restricted';  // Default: 'all'
  servers?: {
    allow?: string[];  // Allowlist of server names
    deny?: string[];   // Denylist of server names
  };
  tools?: {
    [serverName: string]: {
      allow?: string[];  // Allowlist of tool names
      deny?: string[];   // Denylist of tool names
    };
    '*'?: {  // Applies to all servers
      allow?: string[];
      deny?: string[];
    };
  };
};
Deny rules always take precedence over allow rules. If a tool appears in both an allow and a deny list, it will be denied.
Evaluation Order:
  1. Check access mode (if restricted, apply filters)
  2. Check server allowlist/denylist
  3. Check wildcard (*) tool filters
  4. Check server-specific tool filters
  5. Combine results (deny takes precedence)
Examples:
{
  "access": "restricted",
  "servers": {
    "allow": ["filesystem", "time"]
  },
  "tools": {
    "filesystem": {
      "allow": ["read_file", "list_directory"]
    }
  }
}

Via Admin UI

1

Navigate to API Keys

2

Edit key

3

Expand MCP Access Control

4

Configure policy

  • Access Mode: All or Restricted
  • Allowed Servers: Select servers
  • Blocked Servers: Select servers to block
  • Tool Filters: Configure per-server

Via API

Update Key MCP Policy:
PATCH /v1/keys/key-123
Content-Type: application/json
Authorization: Bearer YOUR_ADMIN_KEY

{
  "mcpPolicy": {
    "access": "restricted",
    "servers": {
      "allow": ["filesystem", "time"]
    },
    "tools": {
      "filesystem": {
        "allow": ["read_file", "list_directory"]
      }
    }
  }
}

Session Management

Session Lifecycle

Session Creation:
Client Request → Initialize MCP → Create Session → Store Session ID
Session Reuse:
Client Request → Check Session → Reuse Existing → Update Last Used
Session Expiration:
Idle Timeout (30min) → Mark Expired → Cleanup on Next Request

Session Storage

Sessions stored in Redis:
type McpSession = {
  id: string;
  serverName: string;
  apiKeyId: string;
  createdAt: number;
  lastUsedAt: number;
  expiresAt: number;
  state: 'active' | 'expired';
};
Key: gwai:mcp:session:{apiKeyId}:{serverName}

Per-User Sessions

Each API key gets isolated sessions per server:
API Key A + Server filesystem → Session A1
API Key A + Server git        → Session A2
API Key B + Server filesystem → Session B1
Per-user sessions provide isolation between users, per-user state management, and security and privacy guarantees.

JSON-RPC Proxy

Guardway Gateway proxies JSON-RPC 2.0 messages between clients and MCP servers.

Protocol

{
  "jsonrpc": "2.0",
  "id": "req-123",
  "method": "tools/call",
  "params": {
    "name": "read_file",
    "arguments": {
      "path": "/app/data/file.txt"
    }
  }
}

Proxy Endpoint

POST /v1/mcp/{serverName}/message
Content-Type: application/json
Authorization: Bearer YOUR_API_KEY

{
  "jsonrpc": "2.0",
  "id": "test-1",
  "method": "tools/list",
  "params": {}
}
Response:
{
  "jsonrpc": "2.0",
  "id": "test-1",
  "result": {
    "tools": [
      {
        "name": "read_file",
        "description": "Read file contents",
        "inputSchema": {
          "type": "object",
          "properties": {
            "path": { "type": "string" }
          },
          "required": ["path"]
        }
      }
    ]
  }
}

Supported Methods

Initialization:
  • initialize - Initialize client connection
  • notifications/initialized - Notify initialization complete
Tools:
  • tools/list - List available tools
  • tools/call - Call a tool with arguments
Resources:
  • resources/list - List available resources
  • resources/read - Read resource contents
Prompts:
  • prompts/list - List available prompts
  • prompts/get - Get prompt template

MCP in Chat Completions

Basic Tool Use

Request:
POST /v1/chat/completions
Content-Type: application/json
Authorization: Bearer YOUR_API_KEY

{
  "model": "gpt-4",
  "messages": [
    {
      "role": "user",
      "content": "Read the file /app/data/config.json"
    }
  ],
  "tools": [
    {
      "type": "function",
      "function": {
        "name": "read_file",
        "description": "Read file contents",
        "parameters": {
          "type": "object",
          "properties": {
            "path": { "type": "string" }
          },
          "required": ["path"]
        }
      }
    }
  ]
}
LLM Response (tool call):
{
  "id": "chatcmpl-123",
  "choices": [{
    "message": {
      "role": "assistant",
      "content": null,
      "tool_calls": [{
        "id": "call_abc",
        "type": "function",
        "function": {
          "name": "read_file",
          "arguments": "{\"path\": \"/app/data/config.json\"}"
        }
      }]
    },
    "finish_reason": "tool_calls"
  }]
}
Execute Tool: Client or Guardway Gateway executes tool call via MCP proxy. Submit Result:
POST /v1/chat/completions
Content-Type: application/json
Authorization: Bearer YOUR_API_KEY

{
  "model": "gpt-4",
  "messages": [
    {
      "role": "user",
      "content": "Read the file /app/data/config.json"
    },
    {
      "role": "assistant",
      "content": null,
      "tool_calls": [{
        "id": "call_abc",
        "type": "function",
        "function": {
          "name": "read_file",
          "arguments": "{\"path\": \"/app/data/config.json\"}"
        }
      }]
    },
    {
      "role": "tool",
      "tool_call_id": "call_abc",
      "content": "{\"status\": \"success\", \"content\": \"...file contents...\"}"
    }
  ]
}
Final Response:
{
  "id": "chatcmpl-456",
  "choices": [{
    "message": {
      "role": "assistant",
      "content": "The configuration file contains the following settings: ..."
    },
    "finish_reason": "stop"
  }]
}

Automatic Tool Discovery

Guardway Gateway can automatically discover and inject tools: Request with mcp_auto_tools: true:
POST /v1/chat/completions
Content-Type: application/json
Authorization: Bearer YOUR_API_KEY

{
  "model": "gpt-4",
  "messages": [
    {
      "role": "user",
      "content": "What's the current time?"
    }
  ],
  "mcp_auto_tools": true  # Guardway Gateway discovers and injects tools
}
When mcp_auto_tools is enabled, Guardway Gateway automatically discovers tools from all enabled MCP servers, filters tools based on API key policy, injects tool definitions into the request, and proxies tool calls to the appropriate servers.

Tool Use Examples

{
  "name": "filesystem",
  "mode": "local",
  "enabled": true,
  "local": {
    "command": "npx",
    "args": ["@modelcontextprotocol/server-filesystem", "/app/data"]
  }
}

Troubleshooting MCP

Common Issues

1. Server Won’t Start Symptom: Server status shows “stopped” or “error” Diagnosis:
# Check logs
docker logs agsec-gateway

# Check server status
curl http://localhost:8080/v1/mcp/servers/my-server/status \
  -H "Authorization: Bearer ADMIN_KEY"
Verify that the command is correct and executable, check environment variables, ensure dependencies are installed (npx packages), check file permissions, and review stderr output in logs.
2. stdio Transport Issues Symptom: Server starts but tool calls fail Diagnosis:
  • Check if server logs “running on stdio”
  • Verify JSON-RPC messages are valid
  • Check for npx execution errors
If you encounter npx execution errors, try using node directly instead of npx as a workaround. You can also pre-download packages with npx --yes package-name --help, check npm cache permissions, and verify the package is compatible with stdio transport.
3. HTTP Transport Issues Symptom: Server fails to bind port Diagnosis:
  • Check if port is already in use
  • Verify server logs “Listening on http://”
  • Check firewall rules
Use a different port with --port 8081, kill any existing process on the port, and ensure --host 127.0.0.1 is set.
4. Tool Not Available Symptom: Tool not shown in tools/list Diagnosis:
# List tools
POST /v1/mcp/my-server/message
{
  "jsonrpc": "2.0",
  "id": "1",
  "method": "tools/list",
  "params": {}
}
If a tool is missing, check the allowedTools server configuration, the API key MCP policy, that the server is running, and the tool filtering rules.
5. OAuth Token Errors Symptom: “401 Unauthorized” from remote server Diagnosis:
  • Check token expiration
  • Verify OAuth credentials
  • Check token URL
Refresh the token manually with DELETE /v1/mcp/servers/name/token, verify client ID and secret, check scopes configuration, and test the OAuth flow independently.
6. Session Expiration Symptom: “Session expired” errors
Sessions expire after 30 minutes of inactivity. Create a new session by making a new request. Increasing session timeout is not recommended for security reasons.

Debug Mode

Enable verbose logging:
# Environment variable
LOG_LEVEL=debug

# In code
logger.debug({ name, event: 'mcp_event' }, 'MCP event details');

Metrics Monitoring

Check MCP metrics:
GET /v1/metrics
Authorization: Bearer YOUR_API_KEY
Key Metrics:
  • mcp.total.requests - Total MCP requests
  • mcp.total.errors - Total errors
  • mcp.total.toolsCall - Tool invocations
  • mcp.byServer.{name}.requests - Per-server metrics
  • mcp.total.stdioTimeouts - stdio timeout count

Health Checks

Verify server health:
# Status endpoint
GET /v1/mcp/servers/my-server/status

# Initialize test
POST /v1/mcp/my-server/message
{
  "jsonrpc": "2.0",
  "id": "health-check",
  "method": "initialize",
  "params": {
    "protocolVersion": "2024-11-05",
    "capabilities": {},
    "clientInfo": { "name": "test", "version": "1.0" }
  }
}