loading…
Search for a command to run...
loading…
A FastMCP-based telnet client that enables AI agents to interact with legacy Bulletin Board Systems (BBS) through the Model Context Protocol. It features full A
A FastMCP-based telnet client that enables AI agents to interact with legacy Bulletin Board Systems (BBS) through the Model Context Protocol. It features full ANSI terminal emulation, pattern-based screen reading, and automatic discovery of menus and navigation flows.
FastMCP-based telnet client for BBS (Bulletin Board System) interactions with auto-learning capabilities. Enables AI agents to interact with legacy telnet-based systems through the Model Context Protocol (MCP).
⚠️ AI-Generated Code Disclaimer
This project was generated using AI assistance. While functional, it may contain bugs, security vulnerabilities, or unexpected behavior. Use at your own risk. The author assumes no responsibility or liability for any issues, damages, or losses resulting from the use of this software. Review the code thoroughly before using in any production environment.
bbsbot bridges modern AI agents with vintage BBS systems by providing:
bbsbot uses a clean layered architecture with proper separation of concerns:
graph TB
subgraph "AI Agent"
LLM[Claude/LLM]
MCP[MCP Client]
end
subgraph "bbsbot Server"
App[FastMCP App]
SM[SessionManager]
subgraph "Core Layer"
Session[Session]
end
subgraph "Transport Layer"
Transport[TelnetTransport]
end
subgraph "Terminal Layer"
Emulator[TerminalEmulator<br/>pyte]
end
subgraph "Learning Layer"
Engine[LearningEngine]
Discovery[Menu Discovery]
end
subgraph "Logging Layer"
Logger[SessionLogger]
end
end
subgraph "BBS System"
Telnet[Telnet Server :23]
BBS[BBS Software]
end
subgraph "Knowledge Base"
Prompts[prompt-catalog.md]
Menus[menu-map.md]
Flows[navigation-flows.md]
end
LLM --> MCP
MCP -->|MCP Tools| App
App --> SM
SM --> Session
Session --> Transport
Session --> Emulator
Session --> Engine
Session --> Logger
Transport -->|RFC 854 + IAC Escaping| Telnet
Telnet --> BBS
BBS -->|ANSI/CP437| Telnet
Telnet -->|Raw Bytes| Transport
Transport --> Emulator
Engine --> Discovery
Engine --> Prompts
Engine --> Menus
Engine --> Flows
transport/): Protocol abstraction (telnet with RFC 854 compliance, IAC byte escaping)terminal/): ANSI/CP437 terminal emulation using pytecore/): Session management with resource limits and state isolationlearning/): Auto-discovery of menus/prompts, knowledge base managementlogging/): Structured JSONL session logging with raw bytes[A] Option, prompts Enter name:, and document navigation flowsInstall uv (recommended package installer):
curl -LsSf https://astral.sh/uv/install.sh | sh
Or see uv installation docs for other methods.
bbsbot is an MCP server that must be configured in your MCP client (Claude Desktop, Cline, etc.).
uv tool install bbsbot
Then add to your MCP client configuration:
{
"mcpServers": {
"bbsbot_local_tw2002": {
"command": "bbsbot"
}
}
}
bbsbot provides TW2002 tools locally, but those tools only exist in the MCP
server process you start from this repository/environment. In multi-connector
clients, you may also have unrelated MCP connectors installed; those will not
expose tw2002_* tools.
For swarm runtime/ROI diagnostics and anti-collapse control references, see:
Use an explicit local alias and tool filter for TW2002 operations:
{
"mcpServers": {
"bbsbot_local_tw2002": {
"command": "bbsbot",
"args": ["serve", "--tools", "tw2002"]
}
}
}
If tw2002_* tools are missing, first verify the configured command includes
serve --tools tw2002.
pip install bbsbot
Then configure your MCP client to run bbsbot as a server.
git clone https://github.com/livingstaccato/bbsbot.git
cd bbsbot
uv pip install -e ".[dev]"
Here's a complete example of an AI agent connecting to a BBS, navigating menus, and reading messages:
from fastmcp import Client
from fastmcp.mcp_config import StdioMCPServer
# Start bbsbot server
server = StdioMCPServer(command="bbsbot", args=[])
async with Client(server.to_transport()) as client:
# Connect to BBS
await client.call_tool("bbs_connect", {
"host": "bbs.example.com",
"port": 23,
"cols": 80,
"rows": 25,
"term": "ANSI",
"send_newline": True
})
# Wait for main menu
screen = await client.call_tool("bbs_read_until_pattern", {
"pattern": r"\[M\] Main Menu",
"timeout_ms": 5000
})
# Navigate to messages
await client.call_tool("bbs_send", {"keys": "M\r"})
# Read until message list appears
screen = await client.call_tool("bbs_read_until_pattern", {
"pattern": r"Message #\d+",
"timeout_ms": 3000
})
print(screen["screen"]) # Display the screen
# Disconnect
await client.call_tool("bbs_disconnect", {})
Run as an MCP server to expose BBS tools:
# Start the server (stdio transport)
bbsbot
# Or specify config
bbsbot --host localhost --port 2002
Direct Python API usage without MCP:
from bbsbot.core.session_manager import SessionManager
manager = SessionManager(max_sessions=10)
# Connect and create session
session_id = await manager.create_session(
host="bbs.example.com",
port=23,
cols=80,
rows=25,
term="ANSI",
send_newline=True,
reuse=False
)
# Get session
session = await manager.get_session(session_id)
# Read screen with timeout
snapshot = await session.read(timeout_ms=250, max_bytes=8192)
# Snapshot contains:
# - screen: formatted text (80x25)
# - screen_hash: SHA256 of screen text
# - cursor: {x, y} position
# - cols, rows, term
print(snapshot["screen"])
print(f"Cursor at: {snapshot['cursor']}")
# Send keys
await session.send("A\r\n")
# Wait for specific pattern (manual implementation)
import re
import asyncio
pattern = re.compile(r"Enter your name:")
timeout = 5.0
interval = 0.1
start_time = asyncio.get_event_loop().time()
matched = False
while asyncio.get_event_loop().time() - start_time < timeout:
snapshot = await session.read(timeout_ms=int(interval * 1000), max_bytes=8192)
if pattern.search(snapshot["screen"]):
matched = True
break
await asyncio.sleep(interval)
if matched:
print("Found prompt!")
await session.send("Alice\r")
# Disconnect
await manager.close_session(session_id)
Note: For convenience, use the MCP tools which provide higher-level operations like bbs_read_until_pattern. The direct API is lower-level and requires manual pattern matching loops.
The following tools are exposed when running as an MCP server:
Note: In v0.2.0, we removed duplicate *_screen variants and utility wrappers to simplify the API. Use bbs_read()["screen"] to extract screen text, and compose operations instead of using convenience wrappers like bbs_expect or bbs_play_step.
bbs_connectConnect to a BBS via telnet.
{
"host": "bbs.example.com",
"port": 23,
"cols": 80,
"rows": 25,
"term": "ANSI",
"send_newline": true,
"reuse": false
}
bbs_disconnectDisconnect from the BBS. Always call before exit.
{}
bbs_statusGet connection status, session ID, last RX/TX timestamps, keepalive info.
{}
bbs_readThe primary read method. Reads from telnet stream and returns full snapshot with screen text, raw output, cursor position, and hash. Always logs raw bytes to JSONL so LLM can refer back if uncertain about screen content.
Use timeout_ms=0 to get current screen state without waiting for new data.
{
"timeout_ms": 250,
"max_bytes": 8192
}
Returns:
{
"screen": "formatted 80x25 text...",
"raw": "raw terminal output",
"raw_bytes_b64": "base64 encoded raw bytes",
"screen_hash": "sha256 of screen text",
"cursor": {"x": 0, "y": 0},
"cols": 80,
"rows": 25,
"term": "ANSI"
}
Important: Every bbs_read call logs the full snapshot including raw_bytes_b64 to session.jsonl (if logging enabled). If the LLM misinterprets screen content, it can consult the log file for the exact raw bytes received.
bbs_read_until_nonblankKeep reading until screen has non-whitespace content or timeout.
{
"timeout_ms": 5000,
"interval_ms": 100,
"max_bytes": 8192
}
bbs_read_until_patternRead until screen matches regex pattern.
{
"pattern": "Enter your name:",
"timeout_ms": 5000,
"interval_ms": 100,
"max_bytes": 8192
}
Returns: snapshot with additional "matched": true/false
bbs_sendSend keystrokes to BBS (CP437 encoded).
{
"keys": "A\r\n"
}
Use \r for Enter, \n for Line Feed, \x1b for Escape.
bbs_wakeTry multiple keystroke sequences until screen changes (useful for idle timeouts).
{
"timeout_ms": 5000,
"interval_ms": 250,
"max_bytes": 8192,
"keys_sequence": ["\r", " ", "\r\n"]
}
bbs_auto_learn_enableEnable/disable automatic learning of prompts and menus.
{
"enabled": true
}
bbs_auto_learn_promptsConfigure rules to auto-detect prompts.
{
"rules": [
{
"prompt_id": "username",
"regex": "Enter your name:",
"input_type": "text",
"example_input": "Alice"
}
]
}
bbs_auto_learn_menusConfigure rules to auto-detect menu options.
{
"rules": [
{
"menu_id": "main",
"regex": "\\[M\\] Main Menu"
}
]
}
bbs_auto_learn_discoverEnable automatic discovery of [X] style menu options.
{
"enabled": true
}
bbs_learn_menuManually document a menu.
{
"menu_id": "main",
"title": "Main Menu",
"options": [
{"key": "M", "label": "Read Messages"},
{"key": "P", "label": "Post Message"},
{"key": "Q", "label": "Quit"}
],
"prompt": "Your choice:"
}
bbs_learn_promptManually document a prompt.
{
"prompt_id": "username",
"pattern": "Enter your name:",
"input_type": "text",
"example_input": "Alice",
"notes": "Username for login"
}
bbs_learn_flowDocument navigation between screens.
{
"from_screen": "main_menu",
"action": "M",
"to_screen": "message_list",
"notes": "Press M to read messages"
}
bbs_log_startStart JSONL session logging. Highly recommended - allows LLM to refer back to raw session data if it misreads the screen.
{
"path": "session.jsonl"
}
bbs_log_stopStop session logging.
{}
bbs_log_noteAdd structured note to log for debugging.
{
"note": "Starting message read loop",
"context": "messages"
}
bbs_set_contextSet metadata attached to all subsequent log entries.
{
"context": {
"menu": "main",
"action": "reading_messages"
}
}
bbs_keepaliveConfigure automatic keepalive to prevent idle timeout.
{
"interval_s": 30.0,
"keys": "\r"
}
Set interval_s to 0 or null to disable.
sequenceDiagram
participant LLM as AI Agent (LLM)
participant MCP as bbsbot
participant BBS as BBS System
participant Log as session.jsonl
participant KB as Knowledge Base
LLM->>MCP: bbs_log_start("session.jsonl")
MCP->>Log: Create log file
LLM->>MCP: bbs_connect(host, port, ...)
MCP->>BBS: Telnet connection
BBS-->>MCP: Welcome screen (ANSI)
MCP->>Log: Log connection + raw bytes
LLM->>MCP: bbs_auto_learn_enable(true)
LLM->>MCP: bbs_auto_learn_discover(true)
LLM->>MCP: bbs_read_until_pattern("Main Menu")
MCP->>BBS: Read telnet stream
BBS-->>MCP: Raw bytes + ANSI codes
MCP->>MCP: Parse terminal, extract text
MCP->>Log: Log screen + raw bytes
MCP->>KB: Discover menu options [A], [B]
MCP-->>LLM: screen + cursor + hash
Note over LLM: LLM analyzes screen,<br/>decides to press 'M'
LLM->>MCP: bbs_send("M\r")
MCP->>BBS: Send 'M' + Enter
MCP->>Log: Log keystroke
LLM->>MCP: bbs_read(250, 8192)
BBS-->>MCP: Message list screen
MCP->>Log: Log screen + raw bytes
MCP-->>LLM: Full snapshot
Note over LLM: If LLM is uncertain about<br/>screen content, it can<br/>refer to session.jsonl<br/>for raw bytes
LLM->>MCP: bbs_disconnect()
MCP->>BBS: Close connection
MCP->>Log: Log disconnect
LLM->>MCP: bbs_log_stop()
bbs_log_start) - creates complete record with raw bytesbbs_read for everything - single method that always logs raw data in JSONLbbs_read includes raw_bytes_b64 in session logbbs_read(timeout_ms=0) to get current state.bbs-knowledge/Previously, there were separate methods for reading new data vs. getting current screen. This was confusing and error-prone. Now:
bbs_read always reads, always logs raw bytesraw_bytes_b64 from session.jsonlBy default, learned knowledge is stored in platform-specific user data directories following the XDG Base Directory Specification:
~/.local/share/bbsbot (or $XDG_DATA_HOME/bbsbot)~/Library/Application Support/bbsbot%LOCALAPPDATA%\bbsbotOverride the default location with:
export BBSBOT_KNOWLEDGE_ROOT=/path/to/knowledge
To keep knowledge bases per-project (instead of user-wide):
export BBSBOT_KNOWLEDGE_ROOT=$(pwd)/.bbs-knowledge
Configure terminal size and keepalive via MCP tools:
# Using MCP client
await client.call_tool("bbs_set_size", {"cols": 80, "rows": 25})
await client.call_tool("bbs_keepalive", {"interval_s": 30.0, "keys": "\r"})
Or programmatically:
# Using SessionManager
session = await manager.get_session(session_id)
await session.set_size(cols=80, rows=25)
# Keepalive is configured per-session through the session manager
uv pip install -e ".[dev]"
This project uses modern Python 3.11+ features and strict quality tools:
mypy src/bbsbotruff check src/bbsbotruff format src/bbsbotpytestty src/bbsbotruff check src/bbsbot
ruff format --check src/bbsbot
mypy src/bbsbot
pytest
The architecture and workflow diagrams are generated from Mermaid files using mermaid-py.
Generate SVG diagrams using make:
make diagrams
Available targets:
make diagrams or make diagrams-svg - Generate SVG diagrams (default)make diagrams-png - Generate PNG diagramsmake clean - Remove all generated diagram filesmake help - Show all available targetsAlternatively, run the script directly:
python3 docs/generate_diagrams.py --format svg
Source files:
docs/diagrams/architecture.mmd → architecture.svgdocs/diagrams/workflow.mmd → workflow.svgdocs/diagrams/session-flow.mmd → session-flow.svgbbsbot includes an intelligent bot system that uses prompt detection to autonomously navigate BBS games and test prompt patterns.
Run the TW2002 trading bot:
bbsbot tw2002 bot -c tw2002_bot_config.yaml
When trading.strategy: ai_strategy is active, goal progress is shown:
trading.ai_strategy.visualization_interval turnsTo disable all goal visualization output:
trading:
ai_strategy:
show_goal_visualization: false
You can broadcast the live terminal stream over TCP and attach to it from another terminal:
# Start bot with watch socket enabled (raw ANSI stream)
bbsbot tw2002 bot -c tw2002_bot_config.yaml --watch-socket
# Attach viewer
bbsbot spy
If you want structured JSON events (including goal visualization events), use JSON protocol:
# Start bot with JSON watch protocol
bbsbot tw2002 bot -c tw2002_bot_config.yaml --watch-socket --watch-socket-protocol json
# Attach and view JSON lines (the "viz" events include compact/timeline/summary text)
bbsbot spy --encoding utf-8
Goal visualization watch events look like:
{"event":"viz","data":{"kind":"compact|timeline|summary","text":"...","turn":123,"character_name":"..."}}
If you’re running the bot inside the MCP server process, you can query goal progress on demand:
tw2002_get_goal_visualization (renders compact/timeline/summary)tw2002_get_goal_phases (raw phase data)tw2002_capabilities (returns grouped tool map + quick entrypoints)tw2002_list_sessions / tw2002_set_active_session (deterministic session targeting)tw2002_bootstrap (connect + login in one call)# Run intelligent bot with pattern testing
bbsbot script play_tw2002_intelligent
# Run systematic pattern validation
bbsbot script test_all_patterns
The intelligent bot uses a hybrid reactive approach:
See docs/guides/INTELLIGENT_BOT.md for complete documentation.
from bbsbot.commands.scripts.play_tw2002_intelligent import IntelligentTW2002Bot
bot = IntelligentTW2002Bot()
await bot.connect()
# Navigate to game
await bot.navigate_twgs_to_game()
await bot.enter_game_as_player("BotName")
# Test commands with automatic prompt detection
await bot.test_command("D\r", "Display computer")
await bot.test_command("I\r", "Show inventory")
# Auto-handles pagination
snapshot = await bot.send_and_wait("L\r", "Long range scan")
snapshot = await bot.handle_pagination(snapshot)
# Generate results
await bot.generate_report()
13 patterns tested:
login_username, login_passwordtwgs_main_menu, twgs_select_gamemain_menu, command_prompt_genericsector_command, planet_commandpress_any_key, more_promptyes_no_prompt, quit_confirmenter_numberResults saved to logs/reports/intelligent-bot-{timestamp}.json
UserWarning: Field name "validate" ... shadows an attribute from Pydantic when starting bbsbot. This is currently harmless and does not affect runtime behavior.GNU Affero General Public License v3 or later - Copyright (c) 2025-2026 provide.io llc
See LICENSE for details.
Добавь это в claude_desktop_config.json и перезапусти Claude Desktop.
{
"mcpServers": {
"mcp-bbs": {
"command": "npx",
"args": []
}
}
}Web content fetching and conversion for efficient LLM usage.
Retrieval from AWS Knowledge Base using Bedrock Agent Runtime.
Provides auto-configuration for setting up an MCP server in Spring Boot applications.
A very streamlined mcp client that supports calling and monitoring stdio/sse/streamableHttp, and can also view request responses through the /logs page. It also