ibmi-mcp 1.0.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- ibmi_mcp-1.0.0/.gitignore +9 -0
- ibmi_mcp-1.0.0/LICENSE +24 -0
- ibmi_mcp-1.0.0/PKG-INFO +77 -0
- ibmi_mcp-1.0.0/README.md +65 -0
- ibmi_mcp-1.0.0/pyproject.toml +22 -0
- ibmi_mcp-1.0.0/src/ibmi_mcp/__init__.py +10 -0
- ibmi_mcp-1.0.0/src/ibmi_mcp/__main__.py +8 -0
- ibmi_mcp-1.0.0/src/ibmi_mcp/config.py +19 -0
- ibmi_mcp-1.0.0/src/ibmi_mcp/server.py +195 -0
- ibmi_mcp-1.0.0/src/ibmi_mcp/tn5250/__init__.py +8 -0
- ibmi_mcp-1.0.0/src/ibmi_mcp/tn5250/commands.py +290 -0
- ibmi_mcp-1.0.0/src/ibmi_mcp/tn5250/constants.py +170 -0
- ibmi_mcp-1.0.0/src/ibmi_mcp/tn5250/ebcdic.py +24 -0
- ibmi_mcp-1.0.0/src/ibmi_mcp/tn5250/field.py +62 -0
- ibmi_mcp-1.0.0/src/ibmi_mcp/tn5250/protocol.py +137 -0
- ibmi_mcp-1.0.0/src/ibmi_mcp/tn5250/screen.py +108 -0
- ibmi_mcp-1.0.0/src/ibmi_mcp/tn5250/session.py +527 -0
- ibmi_mcp-1.0.0/src/ibmi_mcp/tn5250/stream.py +124 -0
- ibmi_mcp-1.0.0/tests/__init__.py +0 -0
- ibmi_mcp-1.0.0/tests/test_auto_signon.py +341 -0
- ibmi_mcp-1.0.0/tests/test_cursor_placement.py +225 -0
- ibmi_mcp-1.0.0/tests/test_local_editing.py +362 -0
- ibmi_mcp-1.0.0/tests/test_monocase.py +134 -0
- ibmi_mcp-1.0.0/tests/test_query_reply.py +120 -0
- ibmi_mcp-1.0.0/tests/test_sba_addressing.py +290 -0
- ibmi_mcp-1.0.0/tests/test_sf_parsing.py +287 -0
- ibmi_mcp-1.0.0/tests/test_timeout.py +98 -0
ibmi_mcp-1.0.0/LICENSE
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
BSD 2-Clause License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026, Whitehorn Ltd. Co.
|
|
4
|
+
|
|
5
|
+
Redistribution and use in source and binary forms, with or without
|
|
6
|
+
modification, are permitted provided that the following conditions are met:
|
|
7
|
+
|
|
8
|
+
1. Redistributions of source code must retain the above copyright notice, this
|
|
9
|
+
list of conditions and the following disclaimer.
|
|
10
|
+
|
|
11
|
+
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
12
|
+
this list of conditions and the following disclaimer in the documentation
|
|
13
|
+
and/or other materials provided with the distribution.
|
|
14
|
+
|
|
15
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
16
|
+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
17
|
+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
18
|
+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
19
|
+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
20
|
+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
21
|
+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
22
|
+
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
23
|
+
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
24
|
+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
ibmi_mcp-1.0.0/PKG-INFO
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: ibmi-mcp
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: MCP server providing 5250 terminal access to IBM i systems
|
|
5
|
+
License: BSD-2-Clause
|
|
6
|
+
License-File: LICENSE
|
|
7
|
+
Requires-Python: >=3.10
|
|
8
|
+
Requires-Dist: mcp>=1.0.0
|
|
9
|
+
Requires-Dist: pydantic-settings>=2.0.0
|
|
10
|
+
Requires-Dist: pydantic>=2.0.0
|
|
11
|
+
Description-Content-Type: text/markdown
|
|
12
|
+
|
|
13
|
+
# ibmi-mcp
|
|
14
|
+
|
|
15
|
+
Give your AI Agent terminal access to your IBM i system via TN5250.
|
|
16
|
+
|
|
17
|
+
ibmi-mcp is an MCP server that lets AI Agents like Claude interact with IBM i the same way a human would through a 5250 green-screen terminal.
|
|
18
|
+
|
|
19
|
+
## Installation
|
|
20
|
+
|
|
21
|
+
Install from PyPI:
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
pip install ibmi-mcp
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Then register it with Claude Code:
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
claude mcp add ibmi-5250 -- uvx ibmi-mcp
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Configuration
|
|
34
|
+
|
|
35
|
+
Configure the connection using environment variables:
|
|
36
|
+
|
|
37
|
+
| Variable | Description | Required | Default |
|
|
38
|
+
|----------|-------------|----------|---------|
|
|
39
|
+
| `IBMI_HOST` | IBM i hostname or IP | Yes | — |
|
|
40
|
+
| `IBMI_PORT` | TN5250 port | No | 23 |
|
|
41
|
+
| `IBMI_SSL` | Enable TLS | No | false |
|
|
42
|
+
| `IBMI_USER` | Username for auto-signon | No | — |
|
|
43
|
+
| `IBMI_PASSWORD` | Password for auto-signon | No | — |
|
|
44
|
+
| `IBMI_DEVICE_NAME` | Virtual device name | No | — |
|
|
45
|
+
| `IBMI_CODEPAGE` | EBCDIC codepage | No | cp037 |
|
|
46
|
+
| `IBMI_TERMINAL_TYPE` | Terminal type | No | IBM-3179-2 |
|
|
47
|
+
|
|
48
|
+
When `IBMI_USER` and `IBMI_PASSWORD` are set, ibmi-mcp will automatically sign on when it detects a login screen after connecting.
|
|
49
|
+
|
|
50
|
+
## Tools
|
|
51
|
+
|
|
52
|
+
| Tool | Description |
|
|
53
|
+
|------|-------------|
|
|
54
|
+
| `connect` | Connect to an IBM i system via TN5250 |
|
|
55
|
+
| `disconnect` | Disconnect the active session |
|
|
56
|
+
| `read_screen` | Read the current screen content and input fields |
|
|
57
|
+
| `send_keys` | Type text into the current input field |
|
|
58
|
+
| `send_key` | Send a function/attention key (Enter, F1-F24, PageUp, PageDown, Tab, etc.) |
|
|
59
|
+
| `set_cursor` | Position the cursor at a specific row and column |
|
|
60
|
+
|
|
61
|
+
## Features
|
|
62
|
+
|
|
63
|
+
- **Auto-signon** — Automatically detects sign-on screens and logs in with configured credentials
|
|
64
|
+
- **Full key support** — Enter, F1-F24, PageUp, PageDown, Tab, Backtab, Clear, Help, Print, Attn
|
|
65
|
+
- **Local editing** — Backspace, Delete, Field Exit, Home, and End handled locally for responsive editing
|
|
66
|
+
- **Structured screen data** — Returns screen text, cursor position, and input field metadata
|
|
67
|
+
- **Configurable codepage** — Supports EBCDIC codepage translation (default: CP037)
|
|
68
|
+
|
|
69
|
+
## License
|
|
70
|
+
|
|
71
|
+
BSD 2-Clause. See [LICENSE](LICENSE) for details.
|
|
72
|
+
|
|
73
|
+
## About Whitehorn Ltd. Co.
|
|
74
|
+
|
|
75
|
+
ibmi-mcp is developed and maintained by Whitehorn Ltd. Co., a legacy system modernization firm. We specialize in IBM i, RPG, COBOL, and midrange platforms. We help our clients protect what they can't afford to break while building a path forward.
|
|
76
|
+
|
|
77
|
+
Learn more about how Whitehorn Ltd. Co. can help you modernize your IBM midrange systems at [whitehorn.ltd](https://whitehorn.ltd).
|
ibmi_mcp-1.0.0/README.md
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# ibmi-mcp
|
|
2
|
+
|
|
3
|
+
Give your AI Agent terminal access to your IBM i system via TN5250.
|
|
4
|
+
|
|
5
|
+
ibmi-mcp is an MCP server that lets AI Agents like Claude interact with IBM i the same way a human would through a 5250 green-screen terminal.
|
|
6
|
+
|
|
7
|
+
## Installation
|
|
8
|
+
|
|
9
|
+
Install from PyPI:
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
pip install ibmi-mcp
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
Then register it with Claude Code:
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
claude mcp add ibmi-5250 -- uvx ibmi-mcp
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Configuration
|
|
22
|
+
|
|
23
|
+
Configure the connection using environment variables:
|
|
24
|
+
|
|
25
|
+
| Variable | Description | Required | Default |
|
|
26
|
+
|----------|-------------|----------|---------|
|
|
27
|
+
| `IBMI_HOST` | IBM i hostname or IP | Yes | — |
|
|
28
|
+
| `IBMI_PORT` | TN5250 port | No | 23 |
|
|
29
|
+
| `IBMI_SSL` | Enable TLS | No | false |
|
|
30
|
+
| `IBMI_USER` | Username for auto-signon | No | — |
|
|
31
|
+
| `IBMI_PASSWORD` | Password for auto-signon | No | — |
|
|
32
|
+
| `IBMI_DEVICE_NAME` | Virtual device name | No | — |
|
|
33
|
+
| `IBMI_CODEPAGE` | EBCDIC codepage | No | cp037 |
|
|
34
|
+
| `IBMI_TERMINAL_TYPE` | Terminal type | No | IBM-3179-2 |
|
|
35
|
+
|
|
36
|
+
When `IBMI_USER` and `IBMI_PASSWORD` are set, ibmi-mcp will automatically sign on when it detects a login screen after connecting.
|
|
37
|
+
|
|
38
|
+
## Tools
|
|
39
|
+
|
|
40
|
+
| Tool | Description |
|
|
41
|
+
|------|-------------|
|
|
42
|
+
| `connect` | Connect to an IBM i system via TN5250 |
|
|
43
|
+
| `disconnect` | Disconnect the active session |
|
|
44
|
+
| `read_screen` | Read the current screen content and input fields |
|
|
45
|
+
| `send_keys` | Type text into the current input field |
|
|
46
|
+
| `send_key` | Send a function/attention key (Enter, F1-F24, PageUp, PageDown, Tab, etc.) |
|
|
47
|
+
| `set_cursor` | Position the cursor at a specific row and column |
|
|
48
|
+
|
|
49
|
+
## Features
|
|
50
|
+
|
|
51
|
+
- **Auto-signon** — Automatically detects sign-on screens and logs in with configured credentials
|
|
52
|
+
- **Full key support** — Enter, F1-F24, PageUp, PageDown, Tab, Backtab, Clear, Help, Print, Attn
|
|
53
|
+
- **Local editing** — Backspace, Delete, Field Exit, Home, and End handled locally for responsive editing
|
|
54
|
+
- **Structured screen data** — Returns screen text, cursor position, and input field metadata
|
|
55
|
+
- **Configurable codepage** — Supports EBCDIC codepage translation (default: CP037)
|
|
56
|
+
|
|
57
|
+
## License
|
|
58
|
+
|
|
59
|
+
BSD 2-Clause. See [LICENSE](LICENSE) for details.
|
|
60
|
+
|
|
61
|
+
## About Whitehorn Ltd. Co.
|
|
62
|
+
|
|
63
|
+
ibmi-mcp is developed and maintained by Whitehorn Ltd. Co., a legacy system modernization firm. We specialize in IBM i, RPG, COBOL, and midrange platforms. We help our clients protect what they can't afford to break while building a path forward.
|
|
64
|
+
|
|
65
|
+
Learn more about how Whitehorn Ltd. Co. can help you modernize your IBM midrange systems at [whitehorn.ltd](https://whitehorn.ltd).
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "ibmi-mcp"
|
|
7
|
+
version = "1.0.0"
|
|
8
|
+
description = "MCP server providing 5250 terminal access to IBM i systems"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
license = {text = "BSD-2-Clause"}
|
|
11
|
+
requires-python = ">=3.10"
|
|
12
|
+
dependencies = [
|
|
13
|
+
"mcp>=1.0.0",
|
|
14
|
+
"pydantic>=2.0.0",
|
|
15
|
+
"pydantic-settings>=2.0.0",
|
|
16
|
+
]
|
|
17
|
+
|
|
18
|
+
[project.scripts]
|
|
19
|
+
ibmi-mcp = "ibmi_mcp:main"
|
|
20
|
+
|
|
21
|
+
[tool.hatch.build.targets.wheel]
|
|
22
|
+
packages = ["src/ibmi_mcp"]
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
# ========================================================================
|
|
2
|
+
# Copyright (c) 2026 Whitehorn Ltd. Co.
|
|
3
|
+
# https://whitehorn.ltd
|
|
4
|
+
# ========================================================================
|
|
5
|
+
|
|
6
|
+
from ibmi_mcp.server import mcp
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def main():
|
|
10
|
+
mcp.run(transport="stdio")
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# ========================================================================
|
|
2
|
+
# Copyright (c) 2026 Whitehorn Ltd. Co.
|
|
3
|
+
# https://whitehorn.ltd
|
|
4
|
+
# ========================================================================
|
|
5
|
+
|
|
6
|
+
from pydantic_settings import BaseSettings
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class IBMiConfig(BaseSettings):
|
|
10
|
+
model_config = {"env_prefix": "IBMI_"}
|
|
11
|
+
|
|
12
|
+
host: str = ""
|
|
13
|
+
port: int = 23
|
|
14
|
+
ssl: bool = False
|
|
15
|
+
user: str = ""
|
|
16
|
+
password: str = ""
|
|
17
|
+
device_name: str = ""
|
|
18
|
+
codepage: str = "cp037"
|
|
19
|
+
terminal_type: str = "IBM-3179-2"
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
# ========================================================================
|
|
2
|
+
# Copyright (c) 2026 Whitehorn Ltd. Co.
|
|
3
|
+
# https://whitehorn.ltd
|
|
4
|
+
# ========================================================================
|
|
5
|
+
|
|
6
|
+
from mcp.server.fastmcp import FastMCP
|
|
7
|
+
|
|
8
|
+
from ibmi_mcp.config import IBMiConfig
|
|
9
|
+
from ibmi_mcp.tn5250.session import Tn5250Session
|
|
10
|
+
|
|
11
|
+
mcp = FastMCP("ibmi-5250")
|
|
12
|
+
|
|
13
|
+
_session: Tn5250Session | None = None
|
|
14
|
+
_config = IBMiConfig()
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def _screen_response() -> dict:
|
|
18
|
+
data = _session.screen.get_screen_data()
|
|
19
|
+
if _session.timed_out:
|
|
20
|
+
data["warning"] = "Host did not respond within timeout — screen may be incomplete"
|
|
21
|
+
return data
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
@mcp.tool()
|
|
25
|
+
async def connect(host: str = "", port: int = 0, use_ssl: bool | None = None) -> dict:
|
|
26
|
+
"""Connect to an IBM i system via TN5250.
|
|
27
|
+
|
|
28
|
+
Args:
|
|
29
|
+
host: IBM i hostname or IP. Defaults to IBMI_HOST env var.
|
|
30
|
+
port: TN5250 port. Defaults to IBMI_PORT env var or 23.
|
|
31
|
+
use_ssl: Enable TLS. Defaults to IBMI_SSL env var.
|
|
32
|
+
|
|
33
|
+
Returns the initial screen after connection.
|
|
34
|
+
"""
|
|
35
|
+
global _session
|
|
36
|
+
|
|
37
|
+
if _session is not None and _session.state.value != "disconnected":
|
|
38
|
+
await _session.disconnect()
|
|
39
|
+
|
|
40
|
+
resolved_host = host or _config.host
|
|
41
|
+
if not resolved_host:
|
|
42
|
+
return {"error": "No host specified. Set IBMI_HOST or pass host parameter."}
|
|
43
|
+
|
|
44
|
+
resolved_port = port or _config.port
|
|
45
|
+
resolved_ssl = use_ssl if use_ssl is not None else _config.ssl
|
|
46
|
+
|
|
47
|
+
_session = Tn5250Session(
|
|
48
|
+
host=resolved_host,
|
|
49
|
+
port=resolved_port,
|
|
50
|
+
use_ssl=resolved_ssl,
|
|
51
|
+
terminal_type=_config.terminal_type,
|
|
52
|
+
device_name=_config.device_name,
|
|
53
|
+
codepage=_config.codepage,
|
|
54
|
+
username=_config.user,
|
|
55
|
+
password=_config.password,
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
try:
|
|
59
|
+
await _session.connect()
|
|
60
|
+
except Exception as e:
|
|
61
|
+
_session = None
|
|
62
|
+
return {"error": f"Connection failed: {e}"}
|
|
63
|
+
|
|
64
|
+
return _screen_response()
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
@mcp.tool()
|
|
68
|
+
async def disconnect() -> dict:
|
|
69
|
+
"""Disconnect the active TN5250 session."""
|
|
70
|
+
global _session
|
|
71
|
+
|
|
72
|
+
if _session is None:
|
|
73
|
+
return {"status": "already disconnected"}
|
|
74
|
+
|
|
75
|
+
await _session.disconnect()
|
|
76
|
+
_session = None
|
|
77
|
+
return {"status": "disconnected"}
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
@mcp.tool()
|
|
81
|
+
async def read_screen() -> dict:
|
|
82
|
+
"""Read the current 5250 screen content.
|
|
83
|
+
|
|
84
|
+
Returns structured data with:
|
|
85
|
+
- screen: list of text rows (the visible display)
|
|
86
|
+
- cursor: current cursor position {row, col} (1-based)
|
|
87
|
+
- fields: list of input fields with position, length, value, and type
|
|
88
|
+
- dimensions: screen size {rows, cols}
|
|
89
|
+
"""
|
|
90
|
+
if _session is None:
|
|
91
|
+
return {"error": "Not connected. Call connect() first."}
|
|
92
|
+
|
|
93
|
+
return _session.screen.get_screen_data()
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
@mcp.tool()
|
|
97
|
+
async def send_keys(text: str) -> dict:
|
|
98
|
+
"""Type text into the current input field at the cursor position.
|
|
99
|
+
|
|
100
|
+
Args:
|
|
101
|
+
text: The text to type.
|
|
102
|
+
|
|
103
|
+
Returns the updated screen state.
|
|
104
|
+
"""
|
|
105
|
+
if _session is None:
|
|
106
|
+
return {"error": "Not connected. Call connect() first."}
|
|
107
|
+
|
|
108
|
+
try:
|
|
109
|
+
await _session.type_keys(text)
|
|
110
|
+
except RuntimeError as e:
|
|
111
|
+
return {"error": str(e)}
|
|
112
|
+
|
|
113
|
+
return _screen_response()
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
@mcp.tool()
|
|
117
|
+
async def send_key(key: str) -> dict:
|
|
118
|
+
"""Send an attention/function key and wait for the host response.
|
|
119
|
+
|
|
120
|
+
Args:
|
|
121
|
+
key: Key name. Valid values: Enter, F1-F24, PageUp, PageDown,
|
|
122
|
+
Tab, Backtab, Clear, Help, Print, Attn.
|
|
123
|
+
|
|
124
|
+
Returns the updated screen after the host processes the key.
|
|
125
|
+
"""
|
|
126
|
+
if _session is None:
|
|
127
|
+
return {"error": "Not connected. Call connect() first."}
|
|
128
|
+
|
|
129
|
+
key_lower = key.lower().strip()
|
|
130
|
+
|
|
131
|
+
if key_lower == "attn":
|
|
132
|
+
await _session.send_attention()
|
|
133
|
+
elif key_lower == "tab":
|
|
134
|
+
_move_to_next_field(forward=True)
|
|
135
|
+
elif key_lower in ("backtab", "btab", "shift+tab"):
|
|
136
|
+
_move_to_next_field(forward=False)
|
|
137
|
+
elif _session.handle_local_key(key_lower):
|
|
138
|
+
pass # Handled as local editing key
|
|
139
|
+
else:
|
|
140
|
+
try:
|
|
141
|
+
await _session.send_aid(key_lower)
|
|
142
|
+
except ValueError as e:
|
|
143
|
+
return {"error": str(e)}
|
|
144
|
+
|
|
145
|
+
return _screen_response()
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
@mcp.tool()
|
|
149
|
+
async def set_cursor(row: int, col: int) -> dict:
|
|
150
|
+
"""Position the cursor at the specified location.
|
|
151
|
+
|
|
152
|
+
Args:
|
|
153
|
+
row: Row number (1-based, top row is 1).
|
|
154
|
+
col: Column number (1-based, leftmost column is 1).
|
|
155
|
+
|
|
156
|
+
Returns the updated screen state.
|
|
157
|
+
"""
|
|
158
|
+
if _session is None:
|
|
159
|
+
return {"error": "Not connected. Call connect() first."}
|
|
160
|
+
|
|
161
|
+
if row < 1 or row > _session.screen.rows:
|
|
162
|
+
return {"error": f"Row must be between 1 and {_session.screen.rows}"}
|
|
163
|
+
if col < 1 or col > _session.screen.cols:
|
|
164
|
+
return {"error": f"Col must be between 1 and {_session.screen.cols}"}
|
|
165
|
+
|
|
166
|
+
_session.move_cursor(row, col)
|
|
167
|
+
return _session.screen.get_screen_data()
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
def _move_to_next_field(forward: bool = True) -> None:
|
|
171
|
+
"""Move cursor to the next/previous input field (Tab/Backtab behavior)."""
|
|
172
|
+
if _session is None:
|
|
173
|
+
return
|
|
174
|
+
|
|
175
|
+
fields = _session.screen.get_input_fields()
|
|
176
|
+
if not fields:
|
|
177
|
+
return
|
|
178
|
+
|
|
179
|
+
cursor_pos = _session.screen.cursor_pos()
|
|
180
|
+
cols = _session.screen.cols
|
|
181
|
+
|
|
182
|
+
if forward:
|
|
183
|
+
for f in fields:
|
|
184
|
+
field_start = f.row * cols + f.col
|
|
185
|
+
if field_start > cursor_pos:
|
|
186
|
+
_session.screen.set_cursor(f.row, f.col)
|
|
187
|
+
return
|
|
188
|
+
_session.screen.set_cursor(fields[0].row, fields[0].col)
|
|
189
|
+
else:
|
|
190
|
+
for f in reversed(fields):
|
|
191
|
+
field_start = f.row * cols + f.col
|
|
192
|
+
if field_start < cursor_pos:
|
|
193
|
+
_session.screen.set_cursor(f.row, f.col)
|
|
194
|
+
return
|
|
195
|
+
_session.screen.set_cursor(fields[-1].row, fields[-1].col)
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
# ========================================================================
|
|
2
|
+
# Copyright (c) 2026 Whitehorn Ltd. Co.
|
|
3
|
+
# https://whitehorn.ltd
|
|
4
|
+
# ========================================================================
|
|
5
|
+
|
|
6
|
+
from ibmi_mcp.tn5250.session import Tn5250Session
|
|
7
|
+
|
|
8
|
+
__all__ = ["Tn5250Session"]
|