
PTY
STDIOHaskell MCP server enabling AI agents to control PTY pseudo-terminal connections for CLI automation
Haskell MCP server enabling AI agents to control PTY pseudo-terminal connections for CLI automation
Do not grant unrestricted control to AI.
Unsupervised use or misuse may lead to unintended consequences.
All AI systems must remain strictly under human oversight and control.
Use responsibly, with full awareness and at your own risk.
pty-mcp-server
is a Haskell implementation of an MCP (Model Context Protocol) server that enables AI agents to dynamically acquire and control PTY (pseudo-terminal) sessions, allowing interaction with real system environments through terminal-based interfaces.
The server communicates exclusively via standard input/output (stdio), ensuring simple and secure integration with MCP clients. Through this interface, AI agents can execute commands, retrieve system states, and apply configurations—just as a human operator would through a terminal.
pty-mcp-server
provides the following built-in tools for powerful and flexible automation:
pty-connect
Runs a command via a pseudo-terminal (pty) to interact with external tools or services, with optional arguments.
pty-terminate
Forcefully terminates an active pseudo-terminal (PTY) connection.
pty-message
pms-messages is a tool for sending structured instructions or commands to a running PTY session. It abstracts direct terminal input, allowing the LLM (MCP client) to interact with the PTY process in a controlled and programmable way.
pty-bash
pty-bash is a tool that launches a bash shell in a pseudo terminal (PTY). It allows the LLM (MCP client) to interact with a real Linux shell in an interactive terminal (PTY). This enables AI to run system commands, collect information, and handle prompts or TUI-based tools as if operated by a human, making it effective for dynamic Linux-based automation and diagnostics.
pty-ssh
Establishes an SSH session in a pseudo-terminal with the specified arguments, allowing interaction with remote systems.
pty-telnet
Launches the telnet command within a pseudo-terminal (PTY) session. This allows interactive communication with a remote Telnet server, enabling the AI to respond to prompts such as 'login:' or 'Password:' just like a human user. The PTY environment ensures that the terminal behaves like a real TTY device, which is required for many Telnet servers.
pty-cabal
Launches a cabal repl session within a specified project directory, loading a target Haskell file.
Supports argument passing and live code interaction.
pty-stack
Launches a stack repl session in a pseudo-terminal using the specified project directory, main source file, and arguments.
pty-ghci
Launches a GHCi session in a pseudo-terminal using the specified project directory, main source file, and arguments.
proc-spawn
Spawns an external process using the specified arguments and enables interactive communication via standard input and output. Unlike PTY-based execution, this communicates directly with the process using the runProcess function without allocating a pseudo-terminal. Suitable for non-TUI, stdin/stdout-based interactive programs.
proc-terminate
Forcefully terminates a running process created via runProcess.
proc-message
Sends structured text-based instructions or commands to a subprocess started with runProcess. It provides a programmable interface for interacting with the process via standard input.
proc-cmd
The proc-cmd
tool launches the Windows Command Prompt (cmd.exe
) as a subprocess. It allows the AI to interact with the standard Windows shell environment, enabling execution of batch commands, file operations, and system configuration tasks in a familiar terminal interface.
proc-ps
proc-ps
launches the Windows PowerShell (powershell.exe
) as a subprocess. It provides an interactive command-line environment where the AI can execute PowerShell commands, scripts, and system administration tasks. The shell is started with default options to keep it open and ready for further input.
proc-ssh
proc-ssh
launches an SSH client (ssh
) as a subprocess using runProcess
. It enables the AI to initiate remote connections to other systems via the Secure Shell protocol. The tool can be used to execute remote commands, access remote shells, or tunnel services over SSH. The required arguments
field allows specifying the target user, host, and any SSH options (e.g., -p
, -i
, -L
).
proc-telnet
A tool that runs Telnet sessions by internally using PuTTY's plink executable. This enables interactive Telnet connections on Windows without requiring an external pseudo-terminal emulator like winpty. Users supply the Telnet command arguments, which are passed directly to plink to establish the session. (Note: plink.exe must be available in the system PATH.)
proc-plink
A Windows tool that launches an interactive console application via plink, a command-line SSH and Telnet client. Suitable for executing SSH or Telnet sessions directly without needing an external PTY emulator. (Note: plink.exe must be available in the system PATH.)
socket-open
This tool initiates a socket connection to the specified host and port.
socket-close
This tool close active socket connection that was previously established using the 'socket-opne' tool.
socket-read
Reads the specified number of bytes from the socket. The 'size' parameter indicates how many bytes to read.
socket-write
Write a sequence of bytes to the socket
socket-message
This tool sends a specified string to the active socket connection, then waits for a recognizable prompt from the remote side. Upon detecting the prompt, it captures and returns all output received prior to it.
socket-telnet
A simple Telnet-like communication tool over raw TCP sockets. This tool connects to a specified host and port, sends and receives data, and removes any Telnet IAC (Interpret As Command) sequences from the communication stream. Note: This is a simplified Telnet implementation and does not support full Telnet protocol features.
serial-open
Opens a serial port connection to a specified device with a given baud rate. Commonly used to access on-premises hardware or network devices via console.
serial-close
This tool close active serial connection that was previously established using the 'serial-open' tool.
serial-read
Reads the specified number of bytes from the serial. The 'size' parameter indicates how many bytes to read.
serial-write
Write a sequence of bytes to the serial.
serial-message
This tool sends a specified string to the active socket connection, then waits for a recognizable prompt from the remote side. Upon detecting the prompt, it captures and returns all output received prior to it.
Scriptable CLI Integration
The pty-mcp-server
supports execution of shell scripts associated with registered tools defined in tools-list.json
. Each tool must be registered by name, and a corresponding shell script (.sh
) should exist in the configured tools/
directory.
This design supports AI-driven workflows by exposing tool interfaces through a predictable scripting mechanism. The AI can issue tool invocations by name, and the server transparently manages execution and interaction.
To add a new tool:
your-tool.sh
in the tools/
directory.tools-list.json
with the name "your-tool"
and appropriate metadata.This separation of tool definitions (tools-list.json
) and implementation (tools/your-tool.sh
) ensures clean decoupling and simplifies extensibility.
Note:
Commands starting withpty-
are not supported on Windows. These tools rely on POSIX-style pseudo terminals (PTY), which are not natively available in the Windows environment.
You can build and run pty-mcp-server
using either Podman or Docker.
Note: When running pty-mcp-server inside a Docker container, after establishing a pty connection, you will be operating within the container environment. This should be taken into account when interacting with the server.
Clone the repository and navigate to the docker
directory:
$ git clone https://github.com/phoityne/pty-mcp-server.git $ cd pty-mcp-server/docker $ podman build . -t pty-mcp-server-image $
Ref : build.sh
Run the server inside a container:
$ podman run --rm -i \ --name pty-mcp-server-container \ -v /path/to/dir:/path/to/dir \ --hostname pms-docker-container \ pty-mcp-server-image \ -y /path/to/dir/config.yaml $
Ref : run.sh
Below is an example of how to configure mcp.json
to run the MCP server within VSCode:
{ "servers": { "pty-mcp-server": { "type": "stdio", "command": "/path/to/run.sh", "args": [] /* "command": "podman", "args": [ "run", "--rm", "-i", "--name", "pty-mcp-server-container", "-v", "/path/to/dir:/path/to/dir", "--hostname", "pms-docker-container", "pty-mcp-server-image", "-y", "/path/to/dir/config.yaml" ] */ } } }
If you prefer to build it yourself, make sure the following requirements are met:
You can install pty-mcp-server
using cabal
:
$ cabal install pty-mcp-server
.dxt
PackageYou can also set up the tool using a pre-packaged .dxt
file.
This method is suitable for quick installation into Claude Code or for manual setup via extraction.
🛠️ The
.dxt
package distribution is currently in preparation,
but you can check the latest status and download links at:
https://github.com/phoityne/pms-dxt
The pty-mcp-server
application is executed from the command line.
$ pty-mcp-server -y config.yaml
While the server can be launched directly from the command line, it is typically started and managed by development tools that integrate an MCP client—such as Visual Studio Code. These tools utilize the server to enable interactive and automated command execution via PTY sessions.
.vscode/mcp.json
To streamline development and server invocation from within Visual Studio Code, the project supports a .vscode/mcp.json
configuration file.
This file defines how the pty-mcp-server
should be launched in a development environment. Example configuration:
{ "servers": { "pty-mcp-server": { "type": "stdio", "command": "pty-mcp-server", "args": ["-y", "/path/to/your/config.yaml"] } } }
logDir
:
The directory path where log files will be saved. This includes standard output/error logs and logs from script executions.
logLevel
:
Sets the logging level. Examples include "Debug"
, "Info"
, and "Error"
.
toolsDir
:
Directory containing script files (shell scripts named after tool names, e.g., ping.sh
). If a script matching the tool name exists here, it will be executed when the tool is called.
This directory must also contain the tools-list.json
file, which defines the available public tools and their metadata.
prompts
:
A list of prompt strings used to detect interactive command prompts. This allows the AI to identify when a command is awaiting input. Examples include "ghci>"
, "]$"
, "password:"
, etc.
Ref : socket-telnet-prompt
This video demonstrates a Telnet login sequence powered by the MCP prompt defined in socket-telnet-prompt.md. Using tools like socket-open
, socket-read
, socket-write
, and socket-message
, the AI performs Telnet negotiation, handles prompts, and submits credentials. Binary responses are parsed and displayed in human-readable form.
Ref : serial-nw-setting-prompt
This video demonstrates how pty-mcp-server
enables AI-assisted automation over a serial connection to a network device.
Device Setup
The user specifies the communication port and baud rate.
Example: COM3
, 9600 baud on Windows.
Login Interaction
The AI prompts for a username and password,
and uses them to log in to the network device.
Device Version Retrieval
After logging in, the AI sends a command
to retrieve the installed OS or firmware version.
Online Version Check
The AI accesses the official website to check the latest available version,
and compares it with the installed version.
Session Termination Once the check is complete, the AI logs out and cleanly closes the serial connection.
Ref : Web Service Construction Agent Prompt
[Scene 1: Overview & MCP Configuration]
In this demo, we’ll show how an AI agent builds and runs a web service inside a Docker container using the pty-mcp-server
.
First, we configure mcp.json
to launch the MCP server using a shell script.
This script starts the Docker container where our PTY-based interaction will take place.
[Scene 2: Docker Launch Configuration]
The run.sh
script includes volume mounts, hostname settings, and opens port 8080.
This allows the container to expose a web service to the host system.
[Scene 3: Starting the MCP Server]
Now, the container is launched, and the pty-mcp-server
is running inside it,
ready to handle AI-driven requests through a pseudo-terminal.
[Scene 4: Connecting the AI Agent]
We open the chat interface and send a prompt designed for a web service builder agent.
The AI connects to the container’s Bash session via PTY and begins its preparation.
[Scene 5: Initial Setup Commands]
Following the prompt, the AI starts by:
[Scene 6: AI Ready to Receive Instructions]
Once the environment is ready, we instruct the AI to build a “Hello, world” web service.
From here, the AI begins its autonomous construction process.
[Scene 7: AI Executes Web Setup Commands]
The AI proposes a series of terminal commands.
As the user, we review and approve them one by one.
Steps include:
app.py
) to serve “Hello, world”curl http://localhost:8080
inside the container[Scene 8: Verifying from Outside the Container]
To confirm external accessibility, we access the service from the host via port 8080. As expected, the response is: “Hello, world”
[Scene 9: Reviewing the Execution History]
Finally, we review the AI's actions step by step:
app.py
[Scene 10: Conclusion]
This demonstrates how AI, combined with the PTY-MCP-Server and Docker,
can automate real development tasks — interactively, intelligently, and reproducibly.
#script_add 2 3
, executing the addition.cabal repl
Ref : haskell cabal debug prompt
The architecture of the pty-mcp-server
project is designed with medium-to-large scale systems in mind. Emphasis is placed on modularity, maintainability, and scalability, especially in environments involving multiple teams or organizations.
To achieve these goals, the system is structured as a collection of well-separated packages, each responsible for a specific concern or domain. This package-oriented design provides several strategic benefits.
The overall package structure adheres to the principles of Onion Architecture, reflecting a layered design that places the domain model at the core. Furthermore, the internal module structure within each package is also guided by a layered approach, maintaining clear separation between pure data definitions, domain services, and infrastructure concerns.
pty-mcp-server
as a Dependency InjectorIn addition to managing REPL communication, pty-mcp-server
is not merely an executable module, but also acts as a dependency injector for the entire system.
pty-mcp-server
provides a single point of control over cross-cutting dependencies, improving visibility and control over the system architecture.As a result, inter-package and inter-module dependencies can be centrally coordinated and managed, which promotes better encapsulation, reusability, and testability throughout the system.
This package depends on the following packages:
pms-ui-request
pms-ui-response
pms-ui-notification
pms-infrastructure
pms-infra-cmdrun
pms-infra-procspawn
pms-infra-serial
pms-infra-socket
pms-infra-watch
pms-application-service
pms-domain-service
pms-domain-model
Clear Interface Definition
Each package exposes only its minimal, well-defined public API. This enforces clean module boundaries and reduces unintended dependencies between components.
Team and Vendor Ownership
In larger projects, different teams or external vendors can own specific packages. Clear separation ensures well-defined responsibilities and supports collaborative development across organizational boundaries.
Repository and Release Independence
Packages can be split into separate repositories and versioned independently. This allows for modular development and flexible release workflows, reducing build times and simplifying integration.
Improved Maintainability and Extensibility
By isolating concerns, the impact of code changes is limited to relevant modules. This minimizes regressions and facilitates safe, incremental improvements over time.
This architecture follows a layered and modular approach. Domain models, domain services, application logic, and infrastructure concerns are each encapsulated in their own package, enabling clean composition while preserving separation of responsibilities.