Passer au contenu principal

Call the MCP server from Python

This guide shows a complete Python client for the Casdoor MCP server: it gets a token, connects, calls tools, and handles a missing scope.


Learning outcomes​

  • Get an access token with MCP scopes through the client credentials grant.
  • Initialize a session with the MCP server.
  • Call tools and handle the insufficient_scope error.

What you need​

  • Python 3 with the requests package
  • An application in Casdoor with the client credentials grant turned on in Grant types
  • The client ID and client secret of the application

Run the example​

  1. Save the following program and replace the server URL and the credentials with your own values:

    import requests
    import json

    # Server configuration
    server_url = "https://your-casdoor.com/api/mcp"
    token_url = "https://your-casdoor.com/api/login/oauth/access_token"
    client_id = "your-client-id"
    client_secret = "your-client-secret"

    # Get a scoped token for application management
    token_response = requests.post(token_url, data={
    "grant_type": "client_credentials",
    "client_id": client_id,
    "client_secret": client_secret,
    "scope": "read:application write:application"
    })
    access_token = token_response.json()["access_token"]

    # Create a session with the token
    session = requests.Session()
    session.headers.update({
    "Authorization": f"Bearer {access_token}",
    "Content-Type": "application/json"
    })

    # Initialize the connection
    init_request = {
    "jsonrpc": "2.0",
    "id": 1,
    "method": "initialize",
    "params": {
    "protocolVersion": "2024-11-05",
    "capabilities": {},
    "clientInfo": {"name": "python-client", "version": "1.0.0"}
    }
    }
    response = session.post(server_url, json=init_request)
    print("Initialize:", response.json())

    # Send initialized notification
    notify_request = {
    "jsonrpc": "2.0",
    "method": "notifications/initialized"
    }
    session.post(server_url, json=notify_request)

    # List available tools (filtered by scopes)
    list_tools_request = {
    "jsonrpc": "2.0",
    "id": 2,
    "method": "tools/list"
    }
    response = session.post(server_url, json=list_tools_request)
    print("Available tools:", response.json())

    # Create a new application (requires write:application scope)
    create_app_request = {
    "jsonrpc": "2.0",
    "id": 3,
    "method": "tools/call",
    "params": {
    "name": "add_application",
    "arguments": {
    "application": {
    "owner": "my-org",
    "name": "automated-app",
    "displayName": "Automated Application",
    "organization": "my-org"
    }
    }
    }
    }
    response = session.post(server_url, json=create_app_request)
    result = response.json()

    if "error" in result:
    error = result["error"]
    if error.get("code") == -32001 and error.get("message") == "insufficient_scope":
    # Handle insufficient scope error
    error_data = error.get("data", {})
    print(f"Need scope: {error_data.get('required_scope', 'unknown')}")
    print(f"Have scopes: {error_data.get('granted_scopes', [])}")
    else:
    print(f"Error: {error.get('message', 'Unknown error')}")
    else:
    print("Created application:", result)
  2. Run the program.

The program requests a token with the application:read and application:write scopes, so it can read and change applications. To restrict the client to reading, request only application:read. A write call then fails with insufficient_scope, which the program reports.

See also​