sap-mcp-config 0.0.2__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.
@@ -0,0 +1,2 @@
1
+ *.go text eol=lf
2
+ *.py text eol=lf
@@ -0,0 +1,14 @@
1
+ version: 2
2
+ updates:
3
+ - package-ecosystem: "gomod"
4
+ directory: "/"
5
+ schedule:
6
+ interval: "weekly"
7
+ - package-ecosystem: "pip"
8
+ directory: "/"
9
+ schedule:
10
+ interval: "weekly"
11
+ - package-ecosystem: "github-actions"
12
+ directory: "/"
13
+ schedule:
14
+ interval: "weekly"
@@ -0,0 +1,26 @@
1
+ name: "Go Coverage"
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request: {}
7
+
8
+ jobs:
9
+ coverage:
10
+ runs-on: ubuntu-latest
11
+ steps:
12
+ - name: Install Go
13
+ uses: actions/setup-go@v6
14
+ with:
15
+ go-version: "1.26.x"
16
+ - name: Checkout Code
17
+ uses: actions/checkout@v6
18
+ - name: Calc coverage
19
+ run: go test ./... -v -covermode=count -coverprofile=coverage.out
20
+ - name: Convert coverage to lcov
21
+ uses: jandelgado/gcov2lcov-action@v1.2.0
22
+ - name: Enforce Min Coverage
23
+ uses: VeryGoodOpenSource/very_good_coverage@v3.0.0
24
+ with:
25
+ path: "coverage.lcov"
26
+ min_coverage: 80
@@ -0,0 +1,26 @@
1
+ name: "Go Lint"
2
+
3
+ on:
4
+ pull_request: {}
5
+
6
+ jobs:
7
+ golangci:
8
+ strategy:
9
+ matrix:
10
+ go-version: ["1.26.x"]
11
+ os: [ubuntu-latest]
12
+ runs-on: ${{ matrix.os }}
13
+ name: lint
14
+ steps:
15
+ - name: Install Go
16
+ uses: actions/setup-go@v6
17
+ with:
18
+ go-version: ${{ matrix.go-version }}
19
+ - uses: actions/checkout@v6
20
+ - name: golangci-lint
21
+ uses: golangci/golangci-lint-action@v9
22
+ with:
23
+ version: v2.11.4
24
+ args: --enable dupl,goconst,gocyclo
25
+ - name: fmt
26
+ run: golangci-lint fmt --diff --enable gofmt
@@ -0,0 +1,23 @@
1
+ name: "Go Tests"
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request: {}
7
+
8
+ jobs:
9
+ test:
10
+ strategy:
11
+ matrix:
12
+ go-version: ["1.25.x", "1.26.x"]
13
+ os: [ubuntu-latest]
14
+ runs-on: ${{ matrix.os }}
15
+ steps:
16
+ - name: Install Go
17
+ uses: actions/setup-go@v6
18
+ with:
19
+ go-version: ${{ matrix.go-version }}
20
+ - name: Checkout code
21
+ uses: actions/checkout@v6
22
+ - name: Test
23
+ run: go test ./...
@@ -0,0 +1,22 @@
1
+ name: "Packaging Test"
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request: {}
7
+
8
+ jobs:
9
+ check_packaging:
10
+ runs-on: ubuntu-latest
11
+ steps:
12
+ - uses: actions/checkout@v6
13
+ - name: Set up Python
14
+ uses: actions/setup-python@v6
15
+ with:
16
+ python-version: "3.14"
17
+ - name: Install dependencies
18
+ run: |
19
+ python -m pip install --upgrade pip
20
+ pip install tox
21
+ - name: Run Packaging Test
22
+ run: tox -e test_packaging
@@ -0,0 +1,22 @@
1
+ name: "Python Coverage"
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request: {}
7
+
8
+ jobs:
9
+ coverage:
10
+ runs-on: ubuntu-latest
11
+ steps:
12
+ - uses: actions/checkout@v6
13
+ - name: Set up Python
14
+ uses: actions/setup-python@v6
15
+ with:
16
+ python-version: "3.14"
17
+ - name: Install dependencies
18
+ run: |
19
+ python -m pip install --upgrade pip
20
+ pip install tox
21
+ - name: Run Tests and Record Coverage
22
+ run: tox -e coverage
@@ -0,0 +1,25 @@
1
+ name: "Python Formatting"
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request: {}
7
+
8
+ jobs:
9
+ format:
10
+ runs-on: ubuntu-latest
11
+ strategy:
12
+ matrix:
13
+ tool: ["black", "isort"]
14
+ steps:
15
+ - uses: actions/checkout@v6
16
+ - name: Set up Python
17
+ uses: actions/setup-python@v6
18
+ with:
19
+ python-version: "3.14"
20
+ - name: Install dependencies
21
+ run: |
22
+ python -m pip install --upgrade pip
23
+ pip install .[formatting]
24
+ - name: ${{ matrix.tool }} Code Formatter
25
+ run: ${{ matrix.tool }} . --check
@@ -0,0 +1,26 @@
1
+ name: "Python Lint"
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request: {}
7
+
8
+ jobs:
9
+ pylint:
10
+ name: Python Code Quality and Lint
11
+ runs-on: ubuntu-latest
12
+ strategy:
13
+ matrix:
14
+ linter-env: ["linting", "type_check", "spell_check"]
15
+ steps:
16
+ - uses: actions/checkout@v6
17
+ - name: Set up Python
18
+ uses: actions/setup-python@v6
19
+ with:
20
+ python-version: "3.14"
21
+ - name: Install Dependencies
22
+ run: |
23
+ python -m pip install --upgrade pip
24
+ pip install tox
25
+ - name: Run ${{ matrix.linter-env }} via Tox
26
+ run: tox -e ${{ matrix.linter-env }}
@@ -0,0 +1,45 @@
1
+ name: "Publish to PyPI"
2
+
3
+ on:
4
+ release:
5
+ types: [created, edited]
6
+
7
+ jobs:
8
+ tests:
9
+ if: startsWith(github.ref, 'refs/tags/v')
10
+ runs-on: ubuntu-latest
11
+ steps:
12
+ - uses: actions/checkout@v6
13
+ - name: Set up Python
14
+ uses: actions/setup-python@v6
15
+ with:
16
+ python-version: "3.14"
17
+ - name: Install tox
18
+ run: |
19
+ python -m pip install --upgrade pip
20
+ pip install tox
21
+ - name: Run tox
22
+ run: tox
23
+
24
+ build-n-publish:
25
+ name: Build and publish Python distributions to PyPI
26
+ runs-on: ubuntu-latest
27
+ environment: release
28
+ permissions:
29
+ id-token: write
30
+ needs: tests
31
+ steps:
32
+ - uses: actions/checkout@v6
33
+ - name: Set up Python
34
+ uses: actions/setup-python@v6
35
+ with:
36
+ python-version: "3.14"
37
+ - name: Install dependencies
38
+ run: |
39
+ python -m pip install --upgrade pip
40
+ pip install .[packaging]
41
+ - name: Build wheel and source distributions
42
+ run: python -m build
43
+ - name: Publish distribution to PyPI
44
+ if: startsWith(github.ref, 'refs/tags/v')
45
+ uses: pypa/gh-action-pypi-publish@release/v1
@@ -0,0 +1,26 @@
1
+ name: "Python Tests"
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request: {}
7
+
8
+ jobs:
9
+ pytest:
10
+ runs-on: ${{ matrix.os }}
11
+ strategy:
12
+ matrix:
13
+ python-version: ["3.11", "3.12", "3.13", "3.14"]
14
+ os: [ubuntu-latest]
15
+ steps:
16
+ - uses: actions/checkout@v6
17
+ - name: Set up Python ${{ matrix.python-version }}
18
+ uses: actions/setup-python@v6
19
+ with:
20
+ python-version: ${{ matrix.python-version }}
21
+ - name: Install Dependencies
22
+ run: |
23
+ python -m pip install --upgrade pip
24
+ pip install tox
25
+ - name: Run the Unit Tests via Tox
26
+ run: tox -e tests
@@ -0,0 +1,29 @@
1
+ # Go
2
+ *.exe
3
+ *.exe~
4
+ *.dll
5
+ *.so
6
+ *.dylib
7
+ *.test
8
+ *.out
9
+
10
+ # Python
11
+ __pycache__/
12
+ *.py[cod]
13
+ *.egg-info/
14
+ dist/
15
+ build/
16
+ .tox/
17
+ .mypy_cache/
18
+ htmlcov/
19
+ .coverage
20
+ src/_sap_mcp_config_version.py
21
+
22
+ # IDE
23
+ .idea/
24
+ .vscode/
25
+ *.swp
26
+
27
+ # Env
28
+ .env
29
+ .venv/
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Hochfrequenz Unternehmensberatung GmbH
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,318 @@
1
+ Metadata-Version: 2.4
2
+ Name: sap-mcp-config
3
+ Version: 0.0.2
4
+ Summary: Shared configuration models for MCP servers that connect to SAP systems
5
+ Project-URL: Changelog, https://github.com/Hochfrequenz/sap-mcp-config/releases
6
+ Project-URL: Homepage, https://github.com/Hochfrequenz/sap-mcp-config
7
+ Author-email: Hochfrequenz Unternehmensberatung GmbH <info+github@hochfrequenz.de>
8
+ License: MIT
9
+ License-File: LICENSE
10
+ Keywords: configuration,mcp,sap
11
+ Classifier: Development Status :: 4 - Beta
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: License :: OSI Approved :: MIT License
14
+ Classifier: Operating System :: OS Independent
15
+ Classifier: Programming Language :: Python
16
+ Classifier: Programming Language :: Python :: 3 :: Only
17
+ Classifier: Programming Language :: Python :: 3.11
18
+ Classifier: Programming Language :: Python :: 3.12
19
+ Classifier: Programming Language :: Python :: 3.13
20
+ Classifier: Programming Language :: Python :: 3.14
21
+ Requires-Python: >=3.11
22
+ Requires-Dist: pydantic>=2
23
+ Requires-Dist: python-dotenv>=1
24
+ Provides-Extra: coverage
25
+ Requires-Dist: coverage==7.13.5; extra == 'coverage'
26
+ Provides-Extra: formatting
27
+ Requires-Dist: black==26.3.1; extra == 'formatting'
28
+ Requires-Dist: isort==8.0.1; extra == 'formatting'
29
+ Provides-Extra: linting
30
+ Requires-Dist: pylint==4.0.5; extra == 'linting'
31
+ Provides-Extra: packaging
32
+ Requires-Dist: build==1.4.2; extra == 'packaging'
33
+ Requires-Dist: twine==6.2.0; extra == 'packaging'
34
+ Provides-Extra: spell-check
35
+ Requires-Dist: codespell==2.4.2; extra == 'spell-check'
36
+ Provides-Extra: tests
37
+ Requires-Dist: pytest==9.0.2; extra == 'tests'
38
+ Provides-Extra: type-check
39
+ Requires-Dist: mypy==1.20.0; extra == 'type-check'
40
+ Description-Content-Type: text/markdown
41
+
42
+ # sap-mcp-config
43
+
44
+ ![Go Tests](https://github.com/Hochfrequenz/sap-mcp-config/workflows/Go%20Tests/badge.svg)
45
+ ![Go Coverage](https://github.com/Hochfrequenz/sap-mcp-config/workflows/Go%20Coverage/badge.svg)
46
+ ![Go Lint](https://github.com/Hochfrequenz/sap-mcp-config/workflows/Go%20Lint/badge.svg)
47
+ [![Go Reference](https://pkg.go.dev/badge/github.com/Hochfrequenz/sap-mcp-config.svg)](https://pkg.go.dev/github.com/Hochfrequenz/sap-mcp-config)
48
+ [![Go Report Card](https://goreportcard.com/badge/github.com/Hochfrequenz/sap-mcp-config)](https://goreportcard.com/report/github.com/Hochfrequenz/sap-mcp-config)
49
+ ![Python Tests](https://github.com/Hochfrequenz/sap-mcp-config/workflows/Python%20Tests/badge.svg)
50
+ ![Python Coverage](https://github.com/Hochfrequenz/sap-mcp-config/workflows/Python%20Coverage/badge.svg)
51
+ ![Python Lint](https://github.com/Hochfrequenz/sap-mcp-config/workflows/Python%20Lint/badge.svg)
52
+ ![Python Formatting](https://github.com/Hochfrequenz/sap-mcp-config/workflows/Python%20Formatting/badge.svg)
53
+ ![Python Versions](https://img.shields.io/pypi/pyversions/sap-mcp-config.svg)
54
+ ![PyPI](https://img.shields.io/pypi/v/sap-mcp-config)
55
+
56
+ **The standard way to manage SAP credentials for MCP servers.**
57
+
58
+ If you're building an MCP server that connects to SAP, use this package. It gives you validated, type-safe configuration in both Go and Python with a single shared config file. No more reinventing credential loading, no more inconsistent formats between projects.
59
+
60
+ Both [mcp-server-abap](https://github.com/Hochfrequenz/mcp-server-abap) (Go) and [sapwebgui.mcp](https://github.com/Hochfrequenz/sapwebgui.mcp) (Python) use this package.
61
+
62
+ The default config path (`~/.config/sap-mcp/systems.json`) follows the [XDG Base Directory Specification](https://specifications.freedesktop.org/basedir-spec/latest/).
63
+
64
+ ## Features
65
+
66
+ - **One config file, two languages** — Go and Python read the same JSON, guaranteed by shared test fixtures
67
+ - **Validates eagerly** — reports _all_ errors at once so users fix everything in one pass
68
+ - **Passwords never leak in print/log output** — masked in `str()`/`repr()`/`fmt.Println()`/`fmt.Sprintf("%+v")` (Go: `fmt.Formatter`; Python: `pydantic.SecretStr`)
69
+ - **Immutable after loading** — frozen Pydantic models in Python; in Go, use the returned structs as read-only
70
+ - **`.env` file support** — `SAP_CONFIG_FILE` can be set in a `.env` file
71
+ - **Easy to extend** — subclass `SAPSystem` in Python or embed the struct in Go to add project-specific fields
72
+
73
+ ## MCP JSON Configuration
74
+
75
+ This package integrates naturally with the [MCP JSON configuration standard](https://modelcontextprotocol.io/docs/concepts/transports). Point your MCP server to the shared config file via the `SAP_CONFIG_FILE` environment variable:
76
+
77
+ ```json
78
+ {
79
+ "mcpServers": {
80
+ "sap-abap": {
81
+ "command": "mcp-server-abap",
82
+ "env": {
83
+ "SAP_CONFIG_FILE": "/home/user/.config/sap-mcp/systems.json"
84
+ }
85
+ },
86
+ "sap-webgui": {
87
+ "command": "sapwebgui-mcp",
88
+ "env": {
89
+ "SAP_CONFIG_FILE": "/home/user/.config/sap-mcp/systems.json"
90
+ }
91
+ }
92
+ }
93
+ }
94
+ ```
95
+
96
+ Both servers read the **same config file** with the **same credentials** — no duplication.
97
+
98
+ ## Installation
99
+
100
+ ### Go
101
+
102
+ ```bash
103
+ go get github.com/Hochfrequenz/sap-mcp-config
104
+ ```
105
+
106
+ ### Python
107
+
108
+ ```bash
109
+ pip install sap-mcp-config
110
+ ```
111
+
112
+ ## Configuration File
113
+
114
+ Create `~/.config/sap-mcp/systems.json`:
115
+
116
+ ```json
117
+ {
118
+ "default_system": "dev",
119
+ "systems": {
120
+ "dev": {
121
+ "host": "https://your-sap-system:44300",
122
+ "client": "100",
123
+ "user": "YOUR_USER",
124
+ "password": "YOUR_PASSWORD",
125
+ "language": "DE"
126
+ },
127
+ "prod": {
128
+ "host": "https://prod-sap:44300",
129
+ "client": "200",
130
+ "user": "PROD_USER",
131
+ "password": "PROD_PASSWORD",
132
+ "language": "EN"
133
+ }
134
+ }
135
+ }
136
+ ```
137
+
138
+ Override the config file location via the `SAP_CONFIG_FILE` environment variable:
139
+
140
+ ```bash
141
+ export SAP_CONFIG_FILE=/path/to/my/config.json
142
+ ```
143
+
144
+ This also works from a `.env` file in the current directory.
145
+
146
+ ### Fields
147
+
148
+ | Field | Type | Required | Default | Description |
149
+ |-------|------|----------|---------|-------------|
150
+ | `host` | string | yes | | SAP system base URL (must start with `http://` or `https://`) |
151
+ | `client` | string | no | `""` | SAP client/mandant, must be exactly 3 digits (e.g. `"100"`) |
152
+ | `user` | string | conditional | `""` | SAP username (omit for OAuth2) |
153
+ | `password` | string | conditional | `""` | SAP password (omit for OAuth2) |
154
+ | `language` | string | no | `"EN"` | Login language: `"DE"` or `"EN"` |
155
+ | `tls_skip_verify` | bool | no | `false` | Skip TLS certificate verification |
156
+ | `oauth2_client_id` | string | no | `""` | OAuth2 client ID for token-based auth |
157
+
158
+ **Validation rules:**
159
+ - At least one system must be defined
160
+ - `default_system` must reference an existing system
161
+ - `host` is required and must start with `http://` or `https://`
162
+ - `client`, if set, must be exactly 3 digits
163
+ - `language`, if set, must be `"DE"` or `"EN"`
164
+ - Either both `user` and `password` must be set, or neither (for OAuth2)
165
+
166
+ ## Usage
167
+
168
+ ### Go
169
+
170
+ ```go
171
+ package main
172
+
173
+ import (
174
+ "fmt"
175
+ "os"
176
+
177
+ sapmcpconfig "github.com/Hochfrequenz/sap-mcp-config"
178
+ )
179
+
180
+ func main() {
181
+ // Load from SAP_CONFIG_FILE env var or ~/.config/sap-mcp/systems.json
182
+ cfg, err := sapmcpconfig.LoadDefault()
183
+ if err != nil {
184
+ fmt.Fprintf(os.Stderr, "Configuration error:\n%s\n", err)
185
+ os.Exit(1)
186
+ }
187
+
188
+ // Access the default system
189
+ dev := cfg.GetDefault()
190
+ fmt.Println(dev.Host, dev.Client, dev.User)
191
+
192
+ // Access a specific system
193
+ prod := cfg.Systems["prod"]
194
+ fmt.Println(prod.Host, prod.Client, prod.User)
195
+
196
+ // Password is safe to print — it won't leak
197
+ fmt.Println(dev) // Output: SAPSystem{Host:https://... Client:100 User:DEV_USER Password:*** Language:DE}
198
+ }
199
+ ```
200
+
201
+ ### Python
202
+
203
+ ```python
204
+ import sys
205
+
206
+ from pydantic import ValidationError
207
+
208
+ from sap_mcp_config import load_default
209
+
210
+ try:
211
+ # Load from SAP_CONFIG_FILE env var or ~/.config/sap-mcp/systems.json
212
+ cfg = load_default()
213
+ except FileNotFoundError:
214
+ print("Config file not found. Create ~/.config/sap-mcp/systems.json")
215
+ sys.exit(1)
216
+ except ValidationError as e:
217
+ print(f"Configuration error:\n{e}")
218
+ sys.exit(1)
219
+
220
+ # Access the default system
221
+ dev = cfg.get_default()
222
+ print(dev.host, dev.client, dev.user)
223
+
224
+ # Access a specific system
225
+ prod = cfg.systems["prod"]
226
+ print(prod.host, prod.client, prod.user)
227
+
228
+ # Password is a SecretStr — it won't leak in print/logs
229
+ print(dev) # password=SecretStr('**********')
230
+
231
+ # Access the actual password value when needed
232
+ password = dev.password.get_secret_value()
233
+ ```
234
+
235
+ ### Error Messages
236
+
237
+ Both implementations validate eagerly and return **all** errors at once. A misconfigured file like this:
238
+
239
+ ```json
240
+ {
241
+ "default_system": "missing",
242
+ "systems": {
243
+ "dev": { "host": "ftp://wrong", "client": "1", "user": "u" }
244
+ }
245
+ }
246
+ ```
247
+
248
+ ...will report all problems in a single error:
249
+
250
+ ```
251
+ invalid configuration:
252
+ - default_system "missing" not found in systems
253
+ - system "dev": host must start with http:// or https://, got "ftp://wrong"
254
+ - system "dev": client must be a 3-digit string (e.g. "100"), got "1"
255
+ - system "dev": must have both user and password, or neither (for OAuth2)
256
+ ```
257
+
258
+ This way users fix everything in one pass instead of playing whack-a-mole.
259
+
260
+ ## Extending the Configuration
261
+
262
+ Both languages make it easy to add project-specific fields on top of the shared base.
263
+
264
+ ### Python
265
+
266
+ Subclass `SAPSystem` to add your own fields:
267
+
268
+ ```python
269
+ from pydantic import ConfigDict
270
+ from sap_mcp_config import SAPSystem
271
+
272
+ class MySAPSystem(SAPSystem):
273
+ model_config = ConfigDict() # unfreeze for subclass
274
+
275
+ connection_name: str = ""
276
+ custom_timeout: int = 30
277
+ ```
278
+
279
+ ### Go
280
+
281
+ Embed `SAPSystem` in your own struct:
282
+
283
+ ```go
284
+ type MySAPSystem struct {
285
+ sapmcpconfig.SAPSystem
286
+ ConnectionName string `json:"connection_name"`
287
+ CustomTimeout int `json:"custom_timeout"`
288
+ }
289
+ ```
290
+
291
+ ## Development
292
+
293
+ ### Go
294
+
295
+ ```bash
296
+ go test ./...
297
+ ```
298
+
299
+ ### Python
300
+
301
+ ```bash
302
+ pip install -e ".[tests]"
303
+ PYTHONPATH=src pytest unittests
304
+ ```
305
+
306
+ Or via tox:
307
+
308
+ ```bash
309
+ pip install tox
310
+ tox -e tests # unit tests
311
+ tox -e linting # pylint
312
+ tox -e type_check # mypy --strict
313
+ tox -e coverage # coverage with 80% minimum
314
+ ```
315
+
316
+ ## License
317
+
318
+ MIT