Juniper Junos MCP Server

by juniper

Not rated
GitHub

About

An MCP server for interacting with Juniper Junos network devices using LLMs.

Details

Author
juniper
Categories
Developer Tools, Infrastructure, Other

Config For Claude Desktop Stdio Transport

{ "mcpServers": { "jmcp": { "type": "stdio", "command": "python3", "args": ](https://github.com/astral-sh/uv)["jmcp.py", "-f", "devices.json", "-t", "stdio"] } } }
{ "mcpServers": { "jmcp": { "type": "stdio", "command": "uv", "args": ["run", "python", "jmcp.py", "-f", "devices.json", "-t", "stdio"] } } }

Note:Please provide absolute path for jmcp.py and devices.json file.

Config For Claude Desktop Docker Container

{ "mcpServers": { "jmcp": { "type": "stdio", "command": "/usr/local/bin/docker", "args": [ "run", "--rm", "-i", "-v", "devices.json:/app/config/devices.json", "-v", "vsrx_keypair.pem:/app/config/vsrx_keypair.pem", "junos-mcp-server:latest" ] } } }
docker build -t junos-mcp-server:latest .

By default, the Docker container runs with stdio transport:

docker run --rm -it -v /path/to/your/devices.json:/app/config/devices.json junos-mcp-server:latest

This uses the default command:python jmcp.py -f /app/config/devices.json -t stdio

You can override any arguments by specifying the full command:

docker run --rm -it -v /path/to/your/devices.json:/app/config/devices.json junos-mcp-server:latest python jmcp.py -f /app/config/devices.json -t stdio

Security:the streamable-http transport refuses to start without a valid.tokensfile. Generate one withpython jmcp_token_manager.py generate --id <token-id>and mount it into the container as shown below. SeeAuthenticationfor details, or pass--allow-unauthenticated-httpfor loopback-only local development.

docker run --rm -it \ -v /path/to/your/devices.json:/app/config/devices.json \ -v /path/to/.tokens:/app/.tokens \ -p 30030:30030 \ junos-mcp-server:latest \ python jmcp.py -f /app/config/devices.json -t streamable-http -H 0.0.0.0
docker run --rm -it \ -v /path/to/your/devices.json:/app/config/devices.json \ -v /path/to/.tokens:/app/.tokens \ -p 8080:8080 \ junos-mcp-server:latest \ python jmcp.py -f /app/config/devices.json -t streamable-http -p 8080 -H 0.0.0.0

- Always mount your device configuration file using-v /path/to/you/ devices.json:/app/config/devices.json
- For streamable-http transport, expose the port using-p host_port:container_port
- Mount any SSH private key files if using key-based authentication (e.g.,-v /path/to/key.pem:/app/config/key.pem)

Build docker container for Junos MCP Server

docker build -t junos-mcp-server:latest .

Note:Mount your config filedevices.jsonand mount any other files, in my case I am using pem file for ssh priv key authentication so I am also mounting vsrx_keypair.pem

Junos MCP server supports bothpasswordbased auth as well asSSH keybased authentication (See first 2 routers configs [router-1, router-2]).

{ "router-1": { "ip": "ip-addr", "port": 22, "username": "user", "auth": { "type": "password", "password": "pwd" } }, "router-2": { "ip": "ip-addr", "port": 22, "username": "user", "auth": { "type": "ssh_key", "private_key_path": "/path/to/private/key.pem" } }, "router-3": { "ip": "ip-addr", "port": 22, "username": "user", "ssh_config": "~/.ssh/config_dc", "auth": { "type": "ssh_key", "private_key_path": "/path/to/private/key.pem" } }, "router-4": { "ip": "ip-addr", "port": 22, "username": "user", "ssh_config": "/home/user/.ssh/config_jumphost", "auth": { "type": "password", "password": "pwd" } } }

Junos MCP server also provides support forProxyCommand. (See last 2 routers configs [router-3, router-4]), which enables you to access a target device through an intermediary host that supportsnetcat. This is useful when you can only log in to the target device through the intermediate host (jumphost).

This is an example of an SSH config file being used.ssh/config_jumphost:

# Jumphost VM Connection Host jumphost-vm HostName 10.2.11.200 User root # Used for MCP server IdentityFile /home/user/.ssh/id_rsa_claude IdentitiesOnly yes StrictHostKeyChecking no # cRPD Devices (via jump host) Host dt-crpd1 dtwin-crpd1 digital-twin-crpd1 clab-digital-twin-eop6-pe1 HostName 172.20.20.11 User claude IdentityFile c # ProxyJump jumphost-vm # Not working with JunOS MCP ProxyCommand ssh -l root jumphost-vm nc %h 22 2>/dev/null StrictHostKeyChecking no

Note #1:Portvalue should be an integer (typically22for SSH).

Note #2:IdentityFilerecommendation use full path (e.g/home/user/.ssh /id_rsa_clauderather than~/.ssh/id_rsa_claude).

python3.11 jmcp.py -f devices.json [06/11/25 08:26:11] INFO Starting MCP server 'jmcp-server' with transport 'streamable-http' on http://127.0.0.1:30030/mcp INFO: Started server process [33512] INFO: Waiting for application startup. INFO: Application startup complete. INFO: Uvicorn running on http://127.0.0.1:30030 (Press CTRL+C to quit)
{ "mcp": { "servers": { "my-junos-mcp-server": { "url": "http://127.0.0.1:30030/mcp/" } } } }

Note:You can use VSCode'sCmd+Shift+Pto configure MCP server.

The Junos MCP server supports token-based authentication for secure client access when using streamable-http transport. This prevents unauthorized access to your network infrastructure.

- stdio transport(Claude Desktop): No authentication required - secure by design as it runs locally
- streamable-http transport(VSCode, web clients): Token-based authentication available

The server includes a dedicated token management CLI tool:jmcp_token_manager.py

# Basic token generation python jmcp_token_manager.py generate --id "vscode-dev" # With description python jmcp_token_manager.py generate --id "vscode-dev" --description "VSCode development environment" # Example output: Generated new token: ID: vscode-dev Token: jmcp_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8 Description: VSCode development environment Save this token securely - it won't be shown again!
python jmcp_token_manager.py list # Example output: ID Description Created ------------------------------------------------------------------------------------- vscode-dev VSCode development environment 2025-01-28T10:30:00Z prod-client Production client access 2025-01-28T09:15:00Z
python jmcp_token_manager.py show --id "vscode-dev" # Example output: Token ID: vscode-dev Token: jmcp_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8 Description: VSCode development environment Created: 2025-01-28T10:30:00Z
python jmcp_token_manager.py revoke --id "vscode-dev" # Example output: Token 'vscode-dev' has been revoked

For thestreamable-httptransport the serverfails closed: it refuses to start unless a valid, non-empty.tokensfile is present. The only way to start without tokens is the explicit--allow-unauthenticated-httpflag, which is in turn restricted to loopback binds (127.0.0.1,::1,localhost).

python jmcp.py -f devices.json -t streamable-http INFO - Token-based authentication enabled INFO - Clients must send 'Authorization: Bearer <token>' header INFO - Use jmcp_token_manager.py to manage tokens INFO - Streamable HTTP server started on http://127.0.0.1:30030

Without tokens configured (default - refuses to start):

python jmcp.py -f devices.json -t streamable-http ERROR - Refusing to start streamable-http transport without authentication: .tokens file not found ERROR - Generate a token with: python jmcp_token_manager.py generate --id <token-id> ERROR - Or, for local development on loopback only, re-run with --allow-unauthenticated-http

Explicit unauthenticated mode (loopback only, development only):

python jmcp.py -f devices.json -t streamable-http --allow-unauthenticated-http WARNING -  Streamable HTTP authentication is DISABLED (--allow-unauthenticated-http). .tokens file not found. Server is open to any client that can reach 127.0.0.1:30030 and can commit configuration to mapped devices. Use only for local development.  INFO - Streamable HTTP server started on http://127.0.0.1:30030

Combining--allow-unauthenticated-httpwith a non-loopback bind (for example-H 0.0.0.0) is rejected at startup.

Client Configuration With Authentication

{ "mcp": { "servers": { "my-junos-mcp-server": { "url": "http://127.0.0.1:30030/mcp/", "headers": { "Authorization": "Bearer jmcp_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8" } } } } }
# Test authentication with valid token curl -X POST "http://127.0.0.1:30030/mcp/" \ -H "Authorization: Bearer jmcp_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8" \ -H "Content-Type: application/json" \ -H "Accept: application/json, text/event-stream" \ -d '{"jsonrpc":"2.0","id":1,"method":"tools/list","params":{}}' # Test without token (should fail with 401) curl -X POST "http://127.0.0.1:30030/mcp/" \ -H "Content-Type: application/json" \ -H "Accept: application/json, text/event-stream" \ -d '{"jsonrpc":"2.0","id":1,"method":"tools/list","params":{}}'

Note:MCP streamable-http requires theAccept: application/json, tex /event-streamheader.

When using Docker, mount the.tokensfile to enable authentication:

# Generate token first (outside container) python jmcp_token_manager.py generate --id "docker-client" # Run container with token file mounted docker run --rm -it \ -v /path/to/devices.json:/app/config/devices.json \ -v /path/to/.tokens:/app/.tokens \ -p 30030:30030 \ junos-mcp-server:latest \ python jmcp.py -f /app/config/devices.json -t streamable-http -H 0.0.0.0

- Store tokens securely (password managers, environment variables)
- Use descriptive token IDs for easy management
- Regularly rotate tokens by revoking old ones and generating new ones
- Never commit tokens to version control

- Generate separate tokens for different clients/environments
- Revoke tokens immediately when no longer needed
- Monitor server logs for unauthorized access attempts

- Run streamable-http server behind reverse proxy with HTTPS in production
- Use firewall rules to restrict access to MCP server port
- Consider VPN access for remote clients

The.tokensfile stores tokens in JSON format:

{ "vscode-dev": { "token": "jmcp_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8", "description": "VSCode development environment", "created": "2025-01-28T10:30:00Z" }, "prod-client": { "token": "jmcp_x9y8z7w6v5u4t3s2r1q0p9o8n7m6l5k4j3i2", "description": "Production client access", "created": "2025-01-28T09:15:00Z" } }

Important: Keep this file secure and don't commit it to version control.

Theload_and_commit_configtool now includes a pre-commit guardrail check that validates the submitted candidate configuration against patterns inblock.cfgbefore any device commit actions are attempted.

An MCP server for interacting with Juniper Junos network devices using LLMs.

A Model Context Protocol (MCP) server for Juniper Junos devices that enables LLM interactions with network equipment.

- junos-mcp-server

- Table of Contents
-
Important Security Notice

- Security Requirements
-
Security Best Practices

- Config for Claude Desktop (stdio transport)
-
Config for Claude Desktop (using uv)
-
Config for Claude Desktop (Docker container)

- Build Docker Container
-
Running with Default Settings
-
Overriding Default Arguments

- Start Your Server
-
Point to This URL in Your VSCode Config

- Authentication Behavior
-
Token Management

- Generate a New Token
-
List All Tokens
-
Show Token Value (Recovery)
-
Revoke a Token

- VSCode Configuration with Token
-
Testing with curl
-
Docker with Authentication

- Architecture Overview
-
How Tools Work
-
Adding a New Tool

- Step 1: Create a Handler Function
-
Step 2: Register the Handler
-
Step 3: Define Tool Metadata

Warning:This server enables LLM access to your network infrastructure. Please review these security considerations carefully.

-

Corporate Policy Compliance: Only use this server if your company's policy allows sending data of Junos devices to LLM services.

Server Security: Always secure your Junos MCP server before deployment in production environments.

Authentication: Donotuse password authentication for production deployments. We strongly recommend using SSH key-based authentication for enhanced security.

Deployment Strategy: Until your MCP server is properly secured, only deploy locally for testing purposes. Do not deploy remote servers in production without proper security measures.

- Use SSH key authentication instead of passwords
- Implement proper network access controls
- Monitor and log all MCP server activities
- Regular security audits and updates
- Follow your organization's security policies

Warning:The Junos MCP server supports configuration changes, but please ensure you only use this functionality when you want LLM-generated configurations to be loaded and committed on your Junos router.

Always review the configuration being generated by the LLM and only allow tool execution if it's the correct configuration for your use case.

git clone https://github.com/Juniper/junos-mcp-server.git cd junos-mcp-server pip install -r requirements.txt

If you're usinguv, you can run the server directly:

uv run python jmcp.py -f devices.json -t stdio
python3.11 jmcp.py --help Junos MCP Server options: -h, --help show this help message and exit -f DEVICE_MAPPING, --device-mapping DEVICE_MAPPING the name of the JSON file containing the device mapping -H HOST, --host HOST Junos MCP Server host -t TRANSPORT, --transport TRANSPORT Junos MCP Server transport -p PORT, --port PORT Junos MCP Server port

Junos MCP server supports both streamable-http and stdio transport. Do not use --host with stdio transport.

- JUNOS_TIMEOUT: Command timeout in seconds for Junos CLI operations.
- JMCP_POOL_IDLE_TIMEOUT: Idle timeout in seconds for pooled SSH/NETCONF connections.

- SSH sessions are reused across tool calls via a connection pool; a background cleanup thread (running once a minute) closes connections idle longer than this timeout.
- Default:300. Invalid values are logged and fall back to the default.

Config for Claude Desktop (stdio transport)

{ "mcpServers": { "jmcp": { "type": "stdio", "command": "python3", "args": ["jmcp.py", "-f", "devices.json", "-t", "stdio"] } } }
{ "mcpServers": { "jmcp": { "type": "stdio", "command": "uv", "args": ["run", "python", "jmcp.py", "-f", "devices.json", "-t", "stdio"] } } }

Note:Please provide absolute path for jmcp.py and devices.json file.

Config for Claude Desktop (Docker container)

{ "mcpServers": { "jmcp": { "type": "stdio", "command": "/usr/local/bin/docker", "args": [ "run", "--rm", "-i", "-v", "devices.json:/app/config/devices.json", "-v", "vsrx_keypair.pem:/app/config/vsrx_keypair.pem", "junos-mcp-server:latest" ] } } }
docker build -t junos-mcp-server:latest .

By default, the Docker container runs with stdio transport:

docker run --rm -it -v /path/to/your/devices.json:/app/config/devices.json junos-mcp-server:latest

This uses the default command:python jmcp.py -f /app/config/devices.json -t stdio

You can override any arguments by specifying the full command:

docker run --rm -it -v /path/to/your/devices.json:/app/config/devices.json junos-mcp-server:latest python jmcp.py -f /app/config/devices.json -t stdio

Security:the streamable-http transport refuses to start without a valid.tokensfile. Generate one withpython jmcp_token_manager.py generate --id <token-id>and mount it into the container as shown below. SeeAuthenticationfor details, or pass--allow-unauthenticated-httpfor loopback-only local development.

docker run --rm -it \ -v /path/to/your/devices.json:/app/config/devices.json \ -v /path/to/.tokens:/app/.tokens \ -p 30030:30030 \ junos-mcp-server:latest \ python jmcp.py -f /app/config/devices.json -t streamable-http -H 0.0.0.0
docker run --rm -it \ -v /path/to/your/devices.json:/app/config/devices.json \ -v /path/to/.tokens:/app/.tokens \ -p 8080:8080 \ junos-mcp-server:latest \ python jmcp.py -f /app/config/devices.json -t streamable-http -p 8080 -H 0.0.0.0

- Always mount your device configuration file using-v /path/to/you/ devices.json:/app/config/devices.json
- For streamable-http transport, expose the port using-p host_port:container_port
- Mount any SSH private key files if using key-based authentication (e.g.,-v /path/to/key.pem:/app/config/key.pem)

Build docker container for Junos MCP Server

docker build -t junos-mcp-server:latest .

Note:Mount your config filedevices.jsonand mount any other files, in my case I am using pem file for ssh priv key authentication so I am also mounting vsrx_keypair.pem

Junos MCP server supports bothpasswordbased auth as well asSSH keybased authentication (See first 2 routers configs [router-1, router-2]).

{ "router-1": { "ip": "ip-addr", "port": 22, "username": "user", "auth": { "type": "password", "password": "pwd" } }, "router-2": { "ip": "ip-addr", "port": 22, "username": "user", "auth": { "type": "ssh_key", "private_key_path": "/path/to/private/key.pem" } }, "router-3": { "ip": "ip-addr", "port": 22, "username": "user", "ssh_config": "~/.ssh/config_dc", "auth": { "type": "ssh_key", "private_key_path": "/path/to/private/key.pem" } }, "router-4": { "ip": "ip-addr", "port": 22, "username": "user", "ssh_config": "/home/user/.ssh/config_jumphost", "auth": { "type": "password", "password": "pwd" } } }

Junos MCP server also provides support forProxyCommand. (See last 2 routers configs [router-3, router-4]), which enables you to access a target device through an intermediary host that supportsnetcat. This is useful when you can only log in to the target device through the intermediate host (jumphost).

This is an example of an SSH config file being used.ssh/config_jumphost:

# Jumphost VM Connection Host jumphost-vm HostName 10.2.11.200 User root # Used for MCP server IdentityFile /home/user/.ssh/id_rsa_claude IdentitiesOnly yes StrictHostKeyChecking no # cRPD Devices (via jump host) Host dt-crpd1 dtwin-crpd1 digital-twin-crpd1 clab-digital-twin-eop6-pe1 HostName 172.20.20.11 User claude IdentityFile c # ProxyJump jumphost-vm # Not working with JunOS MCP ProxyCommand ssh -l root jumphost-vm nc %h 22 2>/dev/null StrictHostKeyChecking no

Note #1:Portvalue should be an integer (typically22for SSH).

Note #2:IdentityFilerecommendation use full path (e.g/home/user/.ssh /id_rsa_clauderather than~/.ssh/id_rsa_claude).

python3.11 jmcp.py -f devices.json [06/11/25 08:26:11] INFO Starting MCP server 'jmcp-server' with transport 'streamable-http' on http://127.0.0.1:30030/mcp INFO: Started server process [33512] INFO: Waiting for application startup. INFO: Application startup complete. INFO: Uvicorn running on http://127.0.0.1:30030 (Press CTRL+C to quit)
{ "mcp": { "servers": { "my-junos-mcp-server": { "url": "http://127.0.0.1:30030/mcp/" } } } }

Note:You can use VSCode'sCmd+Shift+Pto configure MCP server.

The Junos MCP server supports token-based authentication for secure client access when using streamable-http transport. This prevents unauthorized access to your network infrastructure.

- stdio transport(Claude Desktop): No authentication required - secure by design as it runs locally
- streamable-http transport(VSCode, web clients): Token-based authentication available

The server includes a dedicated token management CLI tool:jmcp_token_manager.py

# Basic token generation python jmcp_token_manager.py generate --id "vscode-dev" # With description python jmcp_token_manager.py generate --id "vscode-dev" --description "VSCode development environment" # Example output: Generated new token: ID: vscode-dev Token: jmcp_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8 Description: VSCode development environment Save this token securely - it won't be shown again!
python jmcp_token_manager.py list # Example output: ID Description Created ------------------------------------------------------------------------------------- vscode-dev VSCode development environment 2025-01-28T10:30:00Z prod-client Production client access 2025-01-28T09:15:00Z
python jmcp_token_manager.py show --id "vscode-dev" # Example output: Token ID: vscode-dev Token: jmcp_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8 Description: VSCode development environment Created: 2025-01-28T10:30:00Z
python jmcp_token_manager.py revoke --id "vscode-dev" # Example output: Token 'vscode-dev' has been revoked

For thestreamable-httptransport the serverfails closed: it refuses to start unless a valid, non-empty.tokensfile is present. The only way to start without tokens is the explicit--allow-unauthenticated-httpflag, which is in turn restricted to loopback binds (127.0.0.1,::1,localhost).

python jmcp.py -f devices.json -t streamable-http INFO - Token-based authentication enabled INFO - Clients must send 'Authorization: Bearer <token>' header INFO - Use jmcp_token_manager.py to manage tokens INFO - Streamable HTTP server started on http://127.0.0.1:30030

Without tokens configured (default - refuses to start):

python jmcp.py -f devices.json -t streamable-http ERROR - Refusing to start streamable-http transport without authentication: .tokens file not found ERROR - Generate a token with: python jmcp_token_manager.py generate --id <token-id> ERROR - Or, for local development on loopback only, re-run with --allow-unauthenticated-http

Explicit unauthenticated mode (loopback only, development only):

python jmcp.py -f devices.json -t streamable-http --allow-unauthenticated-http WARNING -  Streamable HTTP authentication is DISABLED (--allow-unauthenticated-http). .tokens file not found. Server is open to any client that can reach 127.0.0.1:30030 and can commit configuration to mapped devices. Use only for local development.  INFO - Streamable HTTP server started on http://127.0.0.1:30030

Combining--allow-unauthenticated-httpwith a non-loopback bind (for example-H 0.0.0.0) is rejected at startup.

Client Configuration with Authentication

{ "mcp": { "servers": { "my-junos-mcp-server": { "url": "http://127.0.0.1:30030/mcp/", "headers": { "Authorization": "Bearer jmcp_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8" } } } } }
# Test authentication with valid token curl -X POST "http://127.0.0.1:30030/mcp/" \ -H "Authorization: Bearer jmcp_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8" \ -H "Content-Type: application/json" \ -H "Accept: application/json, text/event-stream" \ -d '{"jsonrpc":"2.0","id":1,"method":"tools/list","params":{}}' # Test without token (should fail with 401) curl -X POST "http://127.0.0.1:30030/mcp/" \ -H "Content-Type: application/json" \ -H "Accept: application/json, text/event-stream" \ -d '{"jsonrpc":"2.0","id":1,"method":"tools/list","params":{}}'

Note:MCP streamable-http requires theAccept: application/json, tex /event-streamheader.

When using Docker, mount the.tokensfile to enable authentication:

# Generate token first (outside container) python jmcp_token_manager.py generate --id "docker-client" # Run container with token file mounted docker run --rm -it \ -v /path/to/devices.json:/app/config/devices.json \ -v /path/to/.tokens:/app/.tokens \ -p 30030:30030 \ junos-mcp-server:latest \ python jmcp.py -f /app/config/devices.json -t streamable-http -H 0.0.0.0

- Store tokens securely (password managers, environment variables)
- Use descriptive token IDs for easy management
- Regularly rotate tokens by revoking old ones and generating new ones
- Never commit tokens to version control

- Generate separate tokens for different clients/environments
- Revoke tokens immediately when no longer needed
- Monitor server logs for unauthorized access attempts

- Run streamable-http server behind reverse proxy with HTTPS in production
- Use firewall rules to restrict access to MCP server port
- Consider VPN access for remote clients

The.tokensfile stores tokens in JSON format:

{ "vscode-dev": { "token": "jmcp_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8", "description": "VSCode development environment", "created": "2025-01-28T10:30:00Z" }, "prod-client": { "token": "jmcp_x9y8z7w6v5u4t3s2r1q0p9o8n7m6l5k4j3i2", "description": "Production client access", "created": "2025-01-28T09:15:00Z" } }

Important: Keep this file secure and don't commit it to version control.

Theload_and_commit_configtool now includes a pre-commit guardrail check that validates the submitted candidate configuration against patterns inblock.cfgbefore any device commit actions are attempted.

No reviews yet — be the first

Sign in to leave a review

Use Google, GitHub, or an email account so ratings stay tied to real people.

Email sign in

No reviews posted yet.