Generic MCP

Use this integration to connect to an MCP server and automatically discover its available tools.

Utilities · Generic MCP MCP

Details

IDGeneric MCP
ProviderOpen Source
CategoryUtilities
From Version8.13.0
Docker Imagedemisto/mcp:1.0.0.10120494

Authentication Type (9 options)

Select the authentication method.

Basic Token Bearer Api-Key RawToken OAuth 2.0 Dynamic Client Registration OAuth 2.0 Authorization Code OAuth 2.0 Client Credentials No Authorization

Connection settings

Parameter Name Type Required Default
Server URL base_url Short text Yes
Authentication Type auth_type Single select Yes
Client ID oauth_credentials Credentials No
Authorization code auth_code Credentials No
Redirect URI redirect_uri Short text No https://oproxy.demisto.ninja/authcode
Custom headers custom_headers Long text No
Authorization Endpoint authorization_endpoint Short text No
Token Endpoint token_endpoint Short text No
Scope scope Short text No
API Token / API Key token Credentials No
Username credentials Credentials No
Server Name server_name Short text No
Trust any certificate (not secure) insecure Boolean No

Commands (4)

An MCP server publishes no fixed command set: list-tools and call-tool are the protocol plumbing, and the tools they reach are discovered from the remote server when the instance connects. The pack cannot name them ahead of time, so neither can this page.

  • call-tool

    Calls a specific tool on the MCP server with optional input parameters.

  • generic-mcp-auth-test

    Test the authentication configuration with the MCP server.

  • generic-mcp-generate-login-url

    Generate an authentication login URL.

  • list-tools

    Retrieves a list of available tools in the MCP server.

import demistomock as demisto
from CommonServerPython import *
from MCPApiModule import *

import asyncio


from urllib.parse import unquote


COMMAND_PREFIX = "generic-mcp"


def validate_required_params(
    base_url: str,
    auth_type: str,
    user_name: str,
    password: str,
    token: str,
    client_id: str,
    client_secret: str,
):
    """Validates that required authentication parameters are present based on authentication type."""
    if auth_type not in AuthMethods.list():
        raise ValueError(f"Unsupported authentication type: {auth_type}")
    if not base_url:
        raise ValueError("Base URL must be provided.")
    if not auth_type:
        raise ValueError("Authentication type must be specified.")
    if auth_type == AuthMethods.BASIC and not all((user_name, password)):
        raise ValueError("Username and Password are required for basic authentication.")
    if auth_type in (AuthMethods.TOKEN, AuthMethods.BEARER, AuthMethods.API_KEY, AuthMethods.RAW_TOKEN) and not token:
        raise ValueError(f"Token is required for {auth_type} authentication.")
    if auth_type in (AuthMethods.CLIENT_CREDENTIALS, AuthMethods.AUTHORIZATION_CODE) and not all((client_id, client_secret)):
        raise ValueError("Client ID, Client Secret are required for OAuth authentication.")


async def main() -> None:  # pragma: no cover
    params = demisto.params()
    args = demisto.args()
    command = demisto.command()

    client = None
    try:
        base_url = params.get("base_url", "")
        auth_type = params.get("auth_type", "")
        user_name = params.get("credentials", {}).get("identifier")
        password = params.get("credentials", {}).get("password")
        token = params.get("token", {}).get("password")
        client_id = params.get("oauth_credentials", {}).get("identifier")
        client_secret = params.get("oauth_credentials", {}).get("password")
        token_endpoint = params.get("token_endpoint", "")
        auth_code = unquote(params.get("auth_code", {}).get("password") or "")
        scope = params.get("scope", "")
        authorization_endpoint = params.get("authorization_endpoint", "")
        redirect_uri = params.get("redirect_uri", "") or REDIRECT_URI
        custom_headers = parse_custom_headers(params.get("custom_headers") or "")
        verify: bool = not argToBoolean(params.get("insecure", False))
        server_name = params.get("server_name", "")

        # Validation is run before creating the client
        validate_required_params(base_url, auth_type, user_name, password, token, client_id, client_secret)

        client = Client(
            base_url=base_url,
            auth_type=auth_type,
            command_prefix=COMMAND_PREFIX,
            user_name=user_name,
            password=password,
            token=token,
            client_id=client_id,
            client_secret=client_secret,
            auth_code=auth_code,
            token_endpoint=token_endpoint,
            scope=scope,
            custom_headers=custom_headers,
            redirect_uri=redirect_uri,
            verify=verify,
        )
        demisto.debug(f"Command being called is {command}")

        if command == "test-module":
            if auth_type in (AuthMethods.AUTHORIZATION_CODE, AuthMethods.DYNAMIC_CLIENT_REGISTRATION):
                raise DemistoException(
                    f"\nTest module is unavailable for this authentication type: {auth_type}.\n"
                    f"Please use the !{COMMAND_PREFIX}-auth-test command to test connectivity.",
                )
            result = await client.test_connection()
            return_results(result)

        elif command == "list-tools":
            result = await client.list_tools(server_name)
            return_results(result)

        elif command == "call-tool":
            result = await client.call_tool(args["name"], args.get("arguments", ""))
            return_results(result)

        elif command == f"{COMMAND_PREFIX}-auth-test":
            result = await client.test_connection(auth_test=True)
            return_results(result)

        elif command == f"{COMMAND_PREFIX}-generate-login-url":
            result = await generate_login_url(
                client._oauth_handler, auth_type, authorization_endpoint, redirect_uri, troubleshooting_redirect=True
            )
            return_results(result)

        else:
            raise NotImplementedError(f"Command {command} is not implemented")

    except BaseException as eg:
        root_msg = extract_root_error_message(eg)
        return_error(f"Failed to execute {command} command.\nError:\n{root_msg}")

    finally:
        if client:
            demisto.debug(f"Closing client connection for {command}")
            await client.close()


""" ENTRY POINT """

if __name__ in ("__main__", "__builtin__", "builtins"):
    asyncio.run(main())