bg-mcpcore 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.
- bg_mcpcore-1.0.0/.gitignore +65 -0
- bg_mcpcore-1.0.0/CHANGELOG.md +3 -0
- bg_mcpcore-1.0.0/LICENSE +21 -0
- bg_mcpcore-1.0.0/PKG-INFO +148 -0
- bg_mcpcore-1.0.0/README.MD +88 -0
- bg_mcpcore-1.0.0/docs/README.template.MD +85 -0
- bg_mcpcore-1.0.0/docs/SECURITY.template.MD +92 -0
- bg_mcpcore-1.0.0/docs/index.md +36 -0
- bg_mcpcore-1.0.0/docs/plugins.md +94 -0
- bg_mcpcore-1.0.0/docs/profiles.md +78 -0
- bg_mcpcore-1.0.0/docs/quickstart.md +62 -0
- bg_mcpcore-1.0.0/docs/security.md +73 -0
- bg_mcpcore-1.0.0/examples/README.md +19 -0
- bg_mcpcore-1.0.0/examples/openapi_server/main.py +20 -0
- bg_mcpcore-1.0.0/examples/openapi_server/petstore-mini.json +18 -0
- bg_mcpcore-1.0.0/examples/openapi_server/profile.json +20 -0
- bg_mcpcore-1.0.0/examples/python_tools_server/main.py +22 -0
- bg_mcpcore-1.0.0/examples/python_tools_server/my_tools.py +24 -0
- bg_mcpcore-1.0.0/examples/python_tools_server/profile.json +7 -0
- bg_mcpcore-1.0.0/examples/registry_server/main.py +10 -0
- bg_mcpcore-1.0.0/examples/registry_server/profile.json +7 -0
- bg_mcpcore-1.0.0/pyproject.toml +206 -0
- bg_mcpcore-1.0.0/src/bg_mcpcore/__init__.py +265 -0
- bg_mcpcore-1.0.0/src/bg_mcpcore/_config_protocols.py +67 -0
- bg_mcpcore-1.0.0/src/bg_mcpcore/app.py +194 -0
- bg_mcpcore-1.0.0/src/bg_mcpcore/auth/__init__.py +27 -0
- bg_mcpcore-1.0.0/src/bg_mcpcore/auth/generic_oidc.py +134 -0
- bg_mcpcore-1.0.0/src/bg_mcpcore/auth/resolvers.py +74 -0
- bg_mcpcore-1.0.0/src/bg_mcpcore/auth/storage.py +130 -0
- bg_mcpcore-1.0.0/src/bg_mcpcore/cli.py +79 -0
- bg_mcpcore-1.0.0/src/bg_mcpcore/extensions/__init__.py +12 -0
- bg_mcpcore-1.0.0/src/bg_mcpcore/extensions/config.py +235 -0
- bg_mcpcore-1.0.0/src/bg_mcpcore/extensions/loader.py +57 -0
- bg_mcpcore-1.0.0/src/bg_mcpcore/extensions/prompts.py +77 -0
- bg_mcpcore-1.0.0/src/bg_mcpcore/extensions/resources.py +91 -0
- bg_mcpcore-1.0.0/src/bg_mcpcore/http/__init__.py +8 -0
- bg_mcpcore-1.0.0/src/bg_mcpcore/http/client.py +117 -0
- bg_mcpcore-1.0.0/src/bg_mcpcore/http/retry.py +60 -0
- bg_mcpcore-1.0.0/src/bg_mcpcore/observability/__init__.py +19 -0
- bg_mcpcore-1.0.0/src/bg_mcpcore/observability/banner.py +56 -0
- bg_mcpcore-1.0.0/src/bg_mcpcore/observability/logging_setup.py +170 -0
- bg_mcpcore-1.0.0/src/bg_mcpcore/observability/sentry.py +51 -0
- bg_mcpcore-1.0.0/src/bg_mcpcore/openapi/__init__.py +15 -0
- bg_mcpcore-1.0.0/src/bg_mcpcore/openapi/loader.py +274 -0
- bg_mcpcore-1.0.0/src/bg_mcpcore/openapi/tool_provider.py +218 -0
- bg_mcpcore-1.0.0/src/bg_mcpcore/plugins.py +207 -0
- bg_mcpcore-1.0.0/src/bg_mcpcore/profile/__init__.py +26 -0
- bg_mcpcore-1.0.0/src/bg_mcpcore/profile/loader.py +96 -0
- bg_mcpcore-1.0.0/src/bg_mcpcore/profile/models.py +110 -0
- bg_mcpcore-1.0.0/src/bg_mcpcore/profile/schema.json +295 -0
- bg_mcpcore-1.0.0/src/bg_mcpcore/providers/__init__.py +21 -0
- bg_mcpcore-1.0.0/src/bg_mcpcore/providers/entra.py +87 -0
- bg_mcpcore-1.0.0/src/bg_mcpcore/providers/generic.py +196 -0
- bg_mcpcore-1.0.0/src/bg_mcpcore/providers/google.py +46 -0
- bg_mcpcore-1.0.0/src/bg_mcpcore/providers/middleware.py +79 -0
- bg_mcpcore-1.0.0/src/bg_mcpcore/providers/protocols.py +32 -0
- bg_mcpcore-1.0.0/src/bg_mcpcore/py.typed +0 -0
- bg_mcpcore-1.0.0/src/bg_mcpcore/server/__init__.py +21 -0
- bg_mcpcore-1.0.0/src/bg_mcpcore/server/middleware.py +131 -0
- bg_mcpcore-1.0.0/src/bg_mcpcore/server/routes.py +89 -0
- bg_mcpcore-1.0.0/src/bg_mcpcore/server/runner.py +60 -0
- bg_mcpcore-1.0.0/src/bg_mcpcore/settings/__init__.py +35 -0
- bg_mcpcore-1.0.0/src/bg_mcpcore/settings/base.py +74 -0
- bg_mcpcore-1.0.0/src/bg_mcpcore/settings/enums.py +14 -0
- bg_mcpcore-1.0.0/src/bg_mcpcore/settings/factory.py +29 -0
- bg_mcpcore-1.0.0/src/bg_mcpcore/settings/helpers.py +86 -0
- bg_mcpcore-1.0.0/src/bg_mcpcore/settings/mixins.py +111 -0
- bg_mcpcore-1.0.0/src/bg_mcpcore/testing/__init__.py +7 -0
- bg_mcpcore-1.0.0/src/bg_mcpcore/testing/plugin.py +49 -0
- bg_mcpcore-1.0.0/src/bg_mcpcore/testing/stubs.py +36 -0
- bg_mcpcore-1.0.0/src/bg_mcpcore/tools/__init__.py +16 -0
- bg_mcpcore-1.0.0/src/bg_mcpcore/tools/protocol.py +71 -0
- bg_mcpcore-1.0.0/src/bg_mcpcore/tools/registry.py +93 -0
- bg_mcpcore-1.0.0/tests/__init__.py +0 -0
- bg_mcpcore-1.0.0/tests/test_assembler.py +52 -0
- bg_mcpcore-1.0.0/tests/test_auth_coverage.py +107 -0
- bg_mcpcore-1.0.0/tests/test_extensions.py +90 -0
- bg_mcpcore-1.0.0/tests/test_extras.py +52 -0
- bg_mcpcore-1.0.0/tests/test_logging.py +45 -0
- bg_mcpcore-1.0.0/tests/test_openapi.py +66 -0
- bg_mcpcore-1.0.0/tests/test_profile.py +115 -0
- bg_mcpcore-1.0.0/tests/test_providers.py +94 -0
- bg_mcpcore-1.0.0/tests/test_providers_generic.py +66 -0
- bg_mcpcore-1.0.0/tests/test_rate_limit.py +103 -0
- bg_mcpcore-1.0.0/tests/test_routes.py +39 -0
- bg_mcpcore-1.0.0/tests/test_settings.py +104 -0
- bg_mcpcore-1.0.0/tests/test_smoke.py +23 -0
- bg_mcpcore-1.0.0/tests/test_storage.py +57 -0
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# Byte-compiled / optimized / DLL files
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[codz]
|
|
4
|
+
*$py.class
|
|
5
|
+
*.so
|
|
6
|
+
|
|
7
|
+
# Distribution / packaging
|
|
8
|
+
.Python
|
|
9
|
+
build/
|
|
10
|
+
dist/
|
|
11
|
+
downloads/
|
|
12
|
+
eggs/
|
|
13
|
+
.eggs/
|
|
14
|
+
sdist/
|
|
15
|
+
wheels/
|
|
16
|
+
*.egg-info/
|
|
17
|
+
*.egg
|
|
18
|
+
MANIFEST
|
|
19
|
+
|
|
20
|
+
# Unit test / coverage reports
|
|
21
|
+
htmlcov/
|
|
22
|
+
.tox/
|
|
23
|
+
.nox/
|
|
24
|
+
.coverage
|
|
25
|
+
.coverage.*
|
|
26
|
+
.cache
|
|
27
|
+
coverage.xml
|
|
28
|
+
*.cover
|
|
29
|
+
.hypothesis/
|
|
30
|
+
.pytest_cache/
|
|
31
|
+
|
|
32
|
+
# Type checkers / linters
|
|
33
|
+
.mypy_cache/
|
|
34
|
+
.dmypy.json
|
|
35
|
+
dmypy.json
|
|
36
|
+
.pyre/
|
|
37
|
+
.pytype/
|
|
38
|
+
.ruff_cache/
|
|
39
|
+
|
|
40
|
+
# Environments
|
|
41
|
+
.env
|
|
42
|
+
.envrc
|
|
43
|
+
.venv
|
|
44
|
+
env/
|
|
45
|
+
venv/
|
|
46
|
+
ENV/
|
|
47
|
+
|
|
48
|
+
# mkdocs
|
|
49
|
+
/site
|
|
50
|
+
|
|
51
|
+
# uv — a library is consumed across environments, so we don't pin a lockfile
|
|
52
|
+
# (the project is pip-native; uv is only a local convenience). Matches the
|
|
53
|
+
# NocoDB_SimpleClient blueprint.
|
|
54
|
+
uv.lock
|
|
55
|
+
|
|
56
|
+
# PyPI
|
|
57
|
+
.pypirc
|
|
58
|
+
|
|
59
|
+
# Tools
|
|
60
|
+
.benchmarks/
|
|
61
|
+
|
|
62
|
+
# IDE
|
|
63
|
+
.vscode/
|
|
64
|
+
.claude/
|
|
65
|
+
.idea/
|
bg_mcpcore-1.0.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 BAUER GROUP
|
|
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,148 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: bg-mcpcore
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: BAUER GROUP MCP framework — config-driven, pluggable REST-API MCP servers on FastMCP
|
|
5
|
+
Project-URL: Homepage, https://github.com/bauer-group/LIB-BG-MCPCore
|
|
6
|
+
Project-URL: Source, https://github.com/bauer-group/LIB-BG-MCPCore
|
|
7
|
+
Project-URL: Issues, https://github.com/bauer-group/LIB-BG-MCPCore/issues
|
|
8
|
+
Project-URL: Changelog, https://github.com/bauer-group/LIB-BG-MCPCore/blob/main/CHANGELOG.md
|
|
9
|
+
Author-email: BAUER GROUP <info@bauer-group.com>, Karl Bauer <karl.bauer@bauer-group.com>
|
|
10
|
+
License-Expression: MIT
|
|
11
|
+
License-File: LICENSE
|
|
12
|
+
Keywords: ai,claude,fastmcp,framework,mcp,oauth,openapi,rest
|
|
13
|
+
Classifier: Development Status :: 4 - Beta
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: Intended Audience :: System Administrators
|
|
16
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
17
|
+
Classifier: Operating System :: OS Independent
|
|
18
|
+
Classifier: Programming Language :: Python :: 3
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
20
|
+
Classifier: Topic :: Internet :: WWW/HTTP
|
|
21
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
22
|
+
Requires-Python: >=3.14
|
|
23
|
+
Requires-Dist: cryptography<49.0.0,>=43.0.0
|
|
24
|
+
Requires-Dist: fastmcp<4.0.0,>=3.0.0
|
|
25
|
+
Requires-Dist: httpx<1.0.0,>=0.28.1
|
|
26
|
+
Requires-Dist: py-key-value-aio[disk]<1.0.0,>=0.4.4
|
|
27
|
+
Requires-Dist: pydantic-settings<3.0.0,>=2.14.1
|
|
28
|
+
Requires-Dist: pydantic<3.0.0,>=2.10.0
|
|
29
|
+
Requires-Dist: rich<15.0.0,>=13.7.0
|
|
30
|
+
Requires-Dist: structlog<26.0.0,>=24.4.0
|
|
31
|
+
Requires-Dist: typer<1.0.0,>=0.15.0
|
|
32
|
+
Provides-Extra: dev
|
|
33
|
+
Requires-Dist: build>=1.0.0; extra == 'dev'
|
|
34
|
+
Requires-Dist: mypy>=1.13.0; extra == 'dev'
|
|
35
|
+
Requires-Dist: pre-commit>=3.5.0; extra == 'dev'
|
|
36
|
+
Requires-Dist: pytest-asyncio<2.0.0,>=0.24.0; extra == 'dev'
|
|
37
|
+
Requires-Dist: pytest-cov<8.0.0,>=6.0.0; extra == 'dev'
|
|
38
|
+
Requires-Dist: pytest-httpx<1.0.0,>=0.35.0; extra == 'dev'
|
|
39
|
+
Requires-Dist: pytest<10.0.0,>=8.3.0; extra == 'dev'
|
|
40
|
+
Requires-Dist: python-semantic-release>=9.0.0; extra == 'dev'
|
|
41
|
+
Requires-Dist: respx<1.0.0,>=0.22.0; extra == 'dev'
|
|
42
|
+
Requires-Dist: ruff>=0.7.0; extra == 'dev'
|
|
43
|
+
Requires-Dist: twine>=5.0.0; extra == 'dev'
|
|
44
|
+
Provides-Extra: docs
|
|
45
|
+
Requires-Dist: mkdocs-material>=9.5.0; extra == 'docs'
|
|
46
|
+
Requires-Dist: mkdocs>=1.6.0; extra == 'docs'
|
|
47
|
+
Requires-Dist: mkdocstrings[python]>=0.26.0; extra == 'docs'
|
|
48
|
+
Provides-Extra: oauth-providers
|
|
49
|
+
Provides-Extra: openapi
|
|
50
|
+
Requires-Dist: pyyaml<7.0.0,>=6.0.0; extra == 'openapi'
|
|
51
|
+
Provides-Extra: redis
|
|
52
|
+
Requires-Dist: py-key-value-aio[redis]<1.0.0,>=0.4.4; extra == 'redis'
|
|
53
|
+
Provides-Extra: tasks
|
|
54
|
+
Requires-Dist: fastmcp[tasks]<4.0.0,>=3.0.0; extra == 'tasks'
|
|
55
|
+
Provides-Extra: testkit
|
|
56
|
+
Requires-Dist: pytest-asyncio<2.0.0,>=0.24.0; extra == 'testkit'
|
|
57
|
+
Requires-Dist: pytest<10.0.0,>=8.3.0; extra == 'testkit'
|
|
58
|
+
Requires-Dist: respx<1.0.0,>=0.22.0; extra == 'testkit'
|
|
59
|
+
Description-Content-Type: text/markdown
|
|
60
|
+
|
|
61
|
+
<!-- AUTO-GENERATED FILE. DO NOT EDIT. Edit docs/README.template.MD instead. -->
|
|
62
|
+
<!-- Generated on 2026-06-01 12:53:32 UTC -->
|
|
63
|
+
|
|
64
|
+
# BAUER GROUP - BG-MCPCore
|
|
65
|
+
|
|
66
|
+
[](https://pypi.org/project/bg-mcpcore/)
|
|
67
|
+
[](https://pypi.org/project/bg-mcpcore/)
|
|
68
|
+
[](https://opensource.org/licenses/MIT)
|
|
69
|
+
[](https://github.com/bauer-group/LIB-BG-MCPCore/actions/workflows/tests.yml)
|
|
70
|
+
|
|
71
|
+
> **BAUER GROUP MCP framework** — config-driven, pluggable REST-API MCP servers on [FastMCP](https://github.com/jlowin/fastmcp).
|
|
72
|
+
|
|
73
|
+
`bg-mcpcore` is the shared foundation for BAUER GROUP's fleet of
|
|
74
|
+
[Model Context Protocol](https://modelcontextprotocol.io) servers that bridge
|
|
75
|
+
REST APIs (Zammad, Shlink, and more). It turns "stand up a secure, OAuth-gated
|
|
76
|
+
MCP server for an API" from a multi-day clone-and-edit job into a short
|
|
77
|
+
declarative profile.
|
|
78
|
+
|
|
79
|
+
**Repository Information:**
|
|
80
|
+
|
|
81
|
+
- **Version:** 0.0.0 (2026-06-01)
|
|
82
|
+
- **Repository:** [bauer-group/LIB-BG-MCPCore](https://github.com/bauer-group/LIB-BG-MCPCore)
|
|
83
|
+
- **Branch:** main
|
|
84
|
+
- **Architecture:** Config-driven, pluggable, secure-by-default
|
|
85
|
+
|
|
86
|
+
## The idea: config for the standard, code for the complex
|
|
87
|
+
|
|
88
|
+
A server is described by a **declarative JSON profile**. Standard behaviour —
|
|
89
|
+
identity, backend connection, inbound/outbound auth, rate limiting,
|
|
90
|
+
observability, health/branding routes, and OpenAPI-derived tools — needs **no
|
|
91
|
+
Python**. The genuinely complex parts (hand-written tools, per-user
|
|
92
|
+
on-behalf-of token flows) drop down to **Python escape hatches** the profile
|
|
93
|
+
points at.
|
|
94
|
+
|
|
95
|
+
```jsonc
|
|
96
|
+
// profiles/mautic.json — a whole server, no Python
|
|
97
|
+
{
|
|
98
|
+
"id": "mautic",
|
|
99
|
+
"display_name": "BAUER GROUP Mautic",
|
|
100
|
+
"backend": { "base_url": "${env:MAUTIC_URL}", "api_base_path": "/api" },
|
|
101
|
+
"auth": {
|
|
102
|
+
"inbound": { "mode": "oidc" },
|
|
103
|
+
"outbound": { "type": "bearer_env", "value_from_env": "MAUTIC_TOKEN" }
|
|
104
|
+
},
|
|
105
|
+
"tools": { "source": "openapi", "spec": { "source": "${env:MAUTIC_OPENAPI_URL}" } }
|
|
106
|
+
}
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
```python
|
|
110
|
+
# src/main.py — the entire entrypoint
|
|
111
|
+
from bg_mcpcore import load_profile, make_cli
|
|
112
|
+
|
|
113
|
+
app = make_cli(load_profile("profiles/mautic.json"))
|
|
114
|
+
if __name__ == "__main__":
|
|
115
|
+
app()
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
## Install
|
|
119
|
+
|
|
120
|
+
```bash
|
|
121
|
+
pip install bg-mcpcore # lean core
|
|
122
|
+
pip install "bg-mcpcore[openapi]" # + OpenAPI tools
|
|
123
|
+
pip install "bg-mcpcore[openapi,redis,tasks,oauth-providers]" # full Shlink-shaped server
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
| Extra | Adds |
|
|
127
|
+
|---|---|
|
|
128
|
+
| `openapi` | `tools.source: "openapi"` — generate the tool surface from an OpenAPI spec |
|
|
129
|
+
| `redis` | Redis-backed encrypted OAuth-state store (multi-replica deployments) |
|
|
130
|
+
| `oauth-providers` | Entra / Google inbound IdP modes |
|
|
131
|
+
| `tasks` | long-running task execution (bulk exports) |
|
|
132
|
+
| `testkit` | reusable pytest fixtures + stubs (pytest11 plugin) |
|
|
133
|
+
|
|
134
|
+
## Architecture
|
|
135
|
+
|
|
136
|
+
- **Pluggable via Python entry points** — new `auth_providers`, `tool_sources`,
|
|
137
|
+
`auth_resolvers` are pip-installable plugins, never core edits.
|
|
138
|
+
- **Central tool registry** — reusable tools (`bg.health`, …) referenced by name
|
|
139
|
+
from any profile (`tools.source: "registry"`).
|
|
140
|
+
- **Fail-closed by design** — `AUTH_MODE=none` forbidden in production; the JWT
|
|
141
|
+
signing key mandatory for any active mode; invariants live in core.
|
|
142
|
+
|
|
143
|
+
See the [documentation](docs/) for the profile schema reference, plugin
|
|
144
|
+
authoring guide, and security model.
|
|
145
|
+
|
|
146
|
+
## License
|
|
147
|
+
|
|
148
|
+
MIT © BAUER GROUP
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
<!-- AUTO-GENERATED FILE. DO NOT EDIT. Edit docs/README.template.MD instead. -->
|
|
2
|
+
<!-- Generated on 2026-06-01 12:53:32 UTC -->
|
|
3
|
+
|
|
4
|
+
# BAUER GROUP - BG-MCPCore
|
|
5
|
+
|
|
6
|
+
[](https://pypi.org/project/bg-mcpcore/)
|
|
7
|
+
[](https://pypi.org/project/bg-mcpcore/)
|
|
8
|
+
[](https://opensource.org/licenses/MIT)
|
|
9
|
+
[](https://github.com/bauer-group/LIB-BG-MCPCore/actions/workflows/tests.yml)
|
|
10
|
+
|
|
11
|
+
> **BAUER GROUP MCP framework** — config-driven, pluggable REST-API MCP servers on [FastMCP](https://github.com/jlowin/fastmcp).
|
|
12
|
+
|
|
13
|
+
`bg-mcpcore` is the shared foundation for BAUER GROUP's fleet of
|
|
14
|
+
[Model Context Protocol](https://modelcontextprotocol.io) servers that bridge
|
|
15
|
+
REST APIs (Zammad, Shlink, and more). It turns "stand up a secure, OAuth-gated
|
|
16
|
+
MCP server for an API" from a multi-day clone-and-edit job into a short
|
|
17
|
+
declarative profile.
|
|
18
|
+
|
|
19
|
+
**Repository Information:**
|
|
20
|
+
|
|
21
|
+
- **Version:** 0.0.0 (2026-06-01)
|
|
22
|
+
- **Repository:** [bauer-group/LIB-BG-MCPCore](https://github.com/bauer-group/LIB-BG-MCPCore)
|
|
23
|
+
- **Branch:** main
|
|
24
|
+
- **Architecture:** Config-driven, pluggable, secure-by-default
|
|
25
|
+
|
|
26
|
+
## The idea: config for the standard, code for the complex
|
|
27
|
+
|
|
28
|
+
A server is described by a **declarative JSON profile**. Standard behaviour —
|
|
29
|
+
identity, backend connection, inbound/outbound auth, rate limiting,
|
|
30
|
+
observability, health/branding routes, and OpenAPI-derived tools — needs **no
|
|
31
|
+
Python**. The genuinely complex parts (hand-written tools, per-user
|
|
32
|
+
on-behalf-of token flows) drop down to **Python escape hatches** the profile
|
|
33
|
+
points at.
|
|
34
|
+
|
|
35
|
+
```jsonc
|
|
36
|
+
// profiles/mautic.json — a whole server, no Python
|
|
37
|
+
{
|
|
38
|
+
"id": "mautic",
|
|
39
|
+
"display_name": "BAUER GROUP Mautic",
|
|
40
|
+
"backend": { "base_url": "${env:MAUTIC_URL}", "api_base_path": "/api" },
|
|
41
|
+
"auth": {
|
|
42
|
+
"inbound": { "mode": "oidc" },
|
|
43
|
+
"outbound": { "type": "bearer_env", "value_from_env": "MAUTIC_TOKEN" }
|
|
44
|
+
},
|
|
45
|
+
"tools": { "source": "openapi", "spec": { "source": "${env:MAUTIC_OPENAPI_URL}" } }
|
|
46
|
+
}
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
```python
|
|
50
|
+
# src/main.py — the entire entrypoint
|
|
51
|
+
from bg_mcpcore import load_profile, make_cli
|
|
52
|
+
|
|
53
|
+
app = make_cli(load_profile("profiles/mautic.json"))
|
|
54
|
+
if __name__ == "__main__":
|
|
55
|
+
app()
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
## Install
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
pip install bg-mcpcore # lean core
|
|
62
|
+
pip install "bg-mcpcore[openapi]" # + OpenAPI tools
|
|
63
|
+
pip install "bg-mcpcore[openapi,redis,tasks,oauth-providers]" # full Shlink-shaped server
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
| Extra | Adds |
|
|
67
|
+
|---|---|
|
|
68
|
+
| `openapi` | `tools.source: "openapi"` — generate the tool surface from an OpenAPI spec |
|
|
69
|
+
| `redis` | Redis-backed encrypted OAuth-state store (multi-replica deployments) |
|
|
70
|
+
| `oauth-providers` | Entra / Google inbound IdP modes |
|
|
71
|
+
| `tasks` | long-running task execution (bulk exports) |
|
|
72
|
+
| `testkit` | reusable pytest fixtures + stubs (pytest11 plugin) |
|
|
73
|
+
|
|
74
|
+
## Architecture
|
|
75
|
+
|
|
76
|
+
- **Pluggable via Python entry points** — new `auth_providers`, `tool_sources`,
|
|
77
|
+
`auth_resolvers` are pip-installable plugins, never core edits.
|
|
78
|
+
- **Central tool registry** — reusable tools (`bg.health`, …) referenced by name
|
|
79
|
+
from any profile (`tools.source: "registry"`).
|
|
80
|
+
- **Fail-closed by design** — `AUTH_MODE=none` forbidden in production; the JWT
|
|
81
|
+
signing key mandatory for any active mode; invariants live in core.
|
|
82
|
+
|
|
83
|
+
See the [documentation](docs/) for the profile schema reference, plugin
|
|
84
|
+
authoring guide, and security model.
|
|
85
|
+
|
|
86
|
+
## License
|
|
87
|
+
|
|
88
|
+
MIT © BAUER GROUP
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
# {{COMPANY_NAME}} - BG-MCPCore
|
|
2
|
+
|
|
3
|
+
[](https://pypi.org/project/bg-mcpcore/)
|
|
4
|
+
[](https://pypi.org/project/bg-mcpcore/)
|
|
5
|
+
[](https://opensource.org/licenses/MIT)
|
|
6
|
+
[](https://github.com/bauer-group/LIB-BG-MCPCore/actions/workflows/tests.yml)
|
|
7
|
+
|
|
8
|
+
> **BAUER GROUP MCP framework** — config-driven, pluggable REST-API MCP servers on [FastMCP](https://github.com/jlowin/fastmcp).
|
|
9
|
+
|
|
10
|
+
`bg-mcpcore` is the shared foundation for BAUER GROUP's fleet of
|
|
11
|
+
[Model Context Protocol](https://modelcontextprotocol.io) servers that bridge
|
|
12
|
+
REST APIs (Zammad, Shlink, and more). It turns "stand up a secure, OAuth-gated
|
|
13
|
+
MCP server for an API" from a multi-day clone-and-edit job into a short
|
|
14
|
+
declarative profile.
|
|
15
|
+
|
|
16
|
+
**Repository Information:**
|
|
17
|
+
|
|
18
|
+
- **Version:** {{VERSION}} ({{DATE}})
|
|
19
|
+
- **Repository:** [{{REPO_FULL_NAME}}]({{REPO_URL}})
|
|
20
|
+
- **Branch:** {{CURRENT_BRANCH}}
|
|
21
|
+
- **Architecture:** Config-driven, pluggable, secure-by-default
|
|
22
|
+
|
|
23
|
+
## The idea: config for the standard, code for the complex
|
|
24
|
+
|
|
25
|
+
A server is described by a **declarative JSON profile**. Standard behaviour —
|
|
26
|
+
identity, backend connection, inbound/outbound auth, rate limiting,
|
|
27
|
+
observability, health/branding routes, and OpenAPI-derived tools — needs **no
|
|
28
|
+
Python**. The genuinely complex parts (hand-written tools, per-user
|
|
29
|
+
on-behalf-of token flows) drop down to **Python escape hatches** the profile
|
|
30
|
+
points at.
|
|
31
|
+
|
|
32
|
+
```jsonc
|
|
33
|
+
// profiles/mautic.json — a whole server, no Python
|
|
34
|
+
{
|
|
35
|
+
"id": "mautic",
|
|
36
|
+
"display_name": "BAUER GROUP Mautic",
|
|
37
|
+
"backend": { "base_url": "${env:MAUTIC_URL}", "api_base_path": "/api" },
|
|
38
|
+
"auth": {
|
|
39
|
+
"inbound": { "mode": "oidc" },
|
|
40
|
+
"outbound": { "type": "bearer_env", "value_from_env": "MAUTIC_TOKEN" }
|
|
41
|
+
},
|
|
42
|
+
"tools": { "source": "openapi", "spec": { "source": "${env:MAUTIC_OPENAPI_URL}" } }
|
|
43
|
+
}
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
```python
|
|
47
|
+
# src/main.py — the entire entrypoint
|
|
48
|
+
from bg_mcpcore import load_profile, make_cli
|
|
49
|
+
|
|
50
|
+
app = make_cli(load_profile("profiles/mautic.json"))
|
|
51
|
+
if __name__ == "__main__":
|
|
52
|
+
app()
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## Install
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
pip install bg-mcpcore # lean core
|
|
59
|
+
pip install "bg-mcpcore[openapi]" # + OpenAPI tools
|
|
60
|
+
pip install "bg-mcpcore[openapi,redis,tasks,oauth-providers]" # full Shlink-shaped server
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
| Extra | Adds |
|
|
64
|
+
|---|---|
|
|
65
|
+
| `openapi` | `tools.source: "openapi"` — generate the tool surface from an OpenAPI spec |
|
|
66
|
+
| `redis` | Redis-backed encrypted OAuth-state store (multi-replica deployments) |
|
|
67
|
+
| `oauth-providers` | Entra / Google inbound IdP modes |
|
|
68
|
+
| `tasks` | long-running task execution (bulk exports) |
|
|
69
|
+
| `testkit` | reusable pytest fixtures + stubs (pytest11 plugin) |
|
|
70
|
+
|
|
71
|
+
## Architecture
|
|
72
|
+
|
|
73
|
+
- **Pluggable via Python entry points** — new `auth_providers`, `tool_sources`,
|
|
74
|
+
`auth_resolvers` are pip-installable plugins, never core edits.
|
|
75
|
+
- **Central tool registry** — reusable tools (`bg.health`, …) referenced by name
|
|
76
|
+
from any profile (`tools.source: "registry"`).
|
|
77
|
+
- **Fail-closed by design** — `AUTH_MODE=none` forbidden in production; the JWT
|
|
78
|
+
signing key mandatory for any active mode; invariants live in core.
|
|
79
|
+
|
|
80
|
+
See the [documentation](docs/) for the profile schema reference, plugin
|
|
81
|
+
authoring guide, and security model.
|
|
82
|
+
|
|
83
|
+
## License
|
|
84
|
+
|
|
85
|
+
MIT © BAUER GROUP
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
# Security Policy
|
|
2
|
+
|
|
3
|
+
## 🚨 Reporting Security Vulnerabilities
|
|
4
|
+
|
|
5
|
+
We take the security of bg-mcpcore seriously. As the shared foundation for
|
|
6
|
+
BAUER GROUP's REST-API MCP servers, a vulnerability here can affect every
|
|
7
|
+
downstream server (bg-zammad-mcp, bg-shlink-mcp, …). Please report responsibly.
|
|
8
|
+
|
|
9
|
+
### 🎯 Preferred Reporting Method
|
|
10
|
+
|
|
11
|
+
Report via **GitHub Security Advisories** — a secure, private channel:
|
|
12
|
+
|
|
13
|
+
1. 🔗 Go to the [Security tab](../../security) of this repository
|
|
14
|
+
2. 🖱️ Click "Report a vulnerability"
|
|
15
|
+
3. 📝 Fill out the advisory form with detailed information
|
|
16
|
+
|
|
17
|
+
### 📧 Alternative
|
|
18
|
+
|
|
19
|
+
- **📬 Email**: security@bauer-group.com
|
|
20
|
+
- **🐛 Issues**: for non-sensitive matters only (use your discretion)
|
|
21
|
+
|
|
22
|
+
### 📋 What to Include
|
|
23
|
+
|
|
24
|
+
1. **📄 Description** of the vulnerability
|
|
25
|
+
2. **💥 Impact** and severity assessment
|
|
26
|
+
3. **🔄 Reproduction** steps
|
|
27
|
+
4. **🖥️ Environment**: affected versions / configuration
|
|
28
|
+
5. **🧪 Proof of Concept** (minimal example, if applicable)
|
|
29
|
+
6. **🔧 Suggested Fix** (if you have one)
|
|
30
|
+
|
|
31
|
+
### ⏱️ Response Timeline
|
|
32
|
+
|
|
33
|
+
- **⚡ Initial Response**: within 48 hours
|
|
34
|
+
- **📊 Status Updates**: weekly during investigation
|
|
35
|
+
- **🎯 Resolution**: Critical 1-7d · High 1-14d · Medium 1-30d · Low 1-90d
|
|
36
|
+
|
|
37
|
+
## 🔐 Supported Versions
|
|
38
|
+
|
|
39
|
+
{{SUPPORTED_VERSIONS_TABLE}}
|
|
40
|
+
|
|
41
|
+
We provide security updates for supported versions only.
|
|
42
|
+
|
|
43
|
+
## 🛡️ Security Model (framework-specific)
|
|
44
|
+
|
|
45
|
+
bg-mcpcore enforces several invariants that downstream servers inherit and
|
|
46
|
+
cannot relax:
|
|
47
|
+
|
|
48
|
+
- **Fail-closed auth**: `AUTH_MODE=none` is forbidden in production, and a JWT
|
|
49
|
+
signing key is mandatory for any active auth mode (`BaseMcpSettings`).
|
|
50
|
+
- **Closed-set auth modes**: an unknown `AUTH_MODE` raises at boot rather than
|
|
51
|
+
silently serving an unauthenticated endpoint.
|
|
52
|
+
- **Secrets never in profiles**: declarative profiles reference secrets only via
|
|
53
|
+
`value_from_env` / `${env:VAR}`; a missing variable fails closed at boot.
|
|
54
|
+
- **Encrypted OAuth state at rest**: DCR/token state is Fernet-encrypted; the
|
|
55
|
+
storage key should be a dedicated `AUTH_STORAGE_ENCRYPTION_KEY` distinct from
|
|
56
|
+
the JWT signing key (a boot warning fires when they are reused).
|
|
57
|
+
- **PII/secret log redaction**: a structured-logging processor masks values
|
|
58
|
+
under sensitive-looking keys; servers extend the fragment list additively.
|
|
59
|
+
- **Outbound-auth fail-closed**: per-call (on-behalf-of) resolvers must raise
|
|
60
|
+
when no credential is available rather than inheriting a static default.
|
|
61
|
+
|
|
62
|
+
### 🤖 Automated Scanning
|
|
63
|
+
|
|
64
|
+
CI runs gitleaks/GitGuardian secret scanning and ruff; dependencies are tracked
|
|
65
|
+
via Dependabot. Contributors should run `ruff`, `mypy --strict`, and `pytest`
|
|
66
|
+
locally before opening a PR.
|
|
67
|
+
|
|
68
|
+
## 📚 Best Practices for Operators
|
|
69
|
+
|
|
70
|
+
1. **🔄 Keep updated** — pin a compatible range (`bg-mcpcore>=X,<X+1`) and let
|
|
71
|
+
Dependabot propose bumps.
|
|
72
|
+
2. **🗝️ Secrets** — store credentials in a secret manager / env vars; never in
|
|
73
|
+
profiles or code. Use a dedicated `AUTH_STORAGE_ENCRYPTION_KEY`.
|
|
74
|
+
3. **🌐 TLS** — keep `verify_tls` enabled against production upstreams.
|
|
75
|
+
4. **🔒 Least privilege** — scope upstream API keys as narrowly as the workflow
|
|
76
|
+
allows; expose only the tools you need.
|
|
77
|
+
|
|
78
|
+
## 📞 Contact
|
|
79
|
+
|
|
80
|
+
- **🛡️ Security Team**: via GitHub Security Advisories
|
|
81
|
+
- **📧 Primary**: security@bauer-group.com
|
|
82
|
+
|
|
83
|
+
---
|
|
84
|
+
|
|
85
|
+
**Current Version**: {{CURRENT_VERSION}}
|
|
86
|
+
**Last Updated**: {{LAST_UPDATED}}
|
|
87
|
+
**Next Review**: {{NEXT_REVIEW}}
|
|
88
|
+
**Policy Version**: {{POLICY_VERSION}}
|
|
89
|
+
|
|
90
|
+
---
|
|
91
|
+
|
|
92
|
+
*🤝 Security is everyone's responsibility.*
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# bg-mcpcore
|
|
2
|
+
|
|
3
|
+
Config-driven, pluggable REST-API MCP servers on [FastMCP](https://github.com/jlowin/fastmcp).
|
|
4
|
+
|
|
5
|
+
bg-mcpcore is the shared foundation for BAUER GROUP's fleet of
|
|
6
|
+
[Model Context Protocol](https://modelcontextprotocol.io) servers that bridge
|
|
7
|
+
REST APIs. A server is described by a **declarative JSON profile**; the genuinely
|
|
8
|
+
complex parts drop down to **Python escape hatches**.
|
|
9
|
+
|
|
10
|
+
## Why
|
|
11
|
+
|
|
12
|
+
The first two MCP servers (Zammad, Shlink) duplicated security-sensitive
|
|
13
|
+
infrastructure — encrypted OAuth-state storage, PII log redaction, rate
|
|
14
|
+
limiting, the OAuth-gated bootstrap — as drifting copies. bg-mcpcore gives that
|
|
15
|
+
code one audited, tested home, and turns a new REST-API MCP server into a
|
|
16
|
+
~15-line profile plus a 4-line entrypoint.
|
|
17
|
+
|
|
18
|
+
## Three complexity tiers
|
|
19
|
+
|
|
20
|
+
| Tier | When | Effort |
|
|
21
|
+
|---|---|---|
|
|
22
|
+
| **1 — pure config** | backend ships a usable OpenAPI spec, standard auth | profile JSON only, no Python |
|
|
23
|
+
| **2 — config + a little Python** | OpenAPI base + a few hand tools, or a custom resolver | profile + a `type: python` reference |
|
|
24
|
+
| **3 — mostly Python** | no usable spec (e.g. Zammad) | `tools.source: python`; the profile still drives auth, rate-limit, identity, routes |
|
|
25
|
+
|
|
26
|
+
## Design pillars
|
|
27
|
+
|
|
28
|
+
- **Modular** — new auth modes / tool sources / resolvers are pip-installable
|
|
29
|
+
plugins registered via Python entry points, never core edits.
|
|
30
|
+
- **Configurable** — every standard behaviour is an overridable profile default.
|
|
31
|
+
- **Stable** — the mandatory core depends only on fastmcp/pydantic/httpx/
|
|
32
|
+
structlog/cryptography; volatile concerns live in optional extras.
|
|
33
|
+
- **Secure** — fail-closed auth invariants are enforced in core and a profile
|
|
34
|
+
cannot switch them off.
|
|
35
|
+
|
|
36
|
+
Start with the [Quickstart](quickstart.md).
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
# Writing plugins
|
|
2
|
+
|
|
3
|
+
bg-mcpcore is extended through Python **entry points** — a new capability is a
|
|
4
|
+
pip-installable package, never a core edit. Three groups:
|
|
5
|
+
|
|
6
|
+
| Group | Adds | Built-ins |
|
|
7
|
+
|---|---|---|
|
|
8
|
+
| `bg_mcpcore.tool_sources` | a `tools.source` value | `python`, `registry`, `openapi` |
|
|
9
|
+
| `bg_mcpcore.auth_providers` | an inbound `AUTH_MODE` | `none`, `oidc` (core); `entra-single`, `entra-multi`, `google`, `auth0`, `aws-cognito`, `clerk`, `descope`, `discord`, `github`, `keycloak`, `oci`, `propelauth`, `scalekit`, `supabase`, `workos` (`[oauth-providers]`) |
|
|
10
|
+
| `bg_mcpcore.auth_middleware` | post-auth gate per mode | `entra-multi` (tenant allowlist) |
|
|
11
|
+
| `bg_mcpcore.auth_resolvers` | an outbound `auth.type` | `none`, `static_header`, `bearer_env`, `python` |
|
|
12
|
+
| `bg_mcpcore.tools` | a named tool for `tools.source: registry` | `bg.ping`, `bg.health` |
|
|
13
|
+
|
|
14
|
+
## Inbound auth providers (full FastMCP parity)
|
|
15
|
+
|
|
16
|
+
`AUTH_MODE=oidc` covers any standard-OIDC IdP via discovery (Keycloak, Auth0,
|
|
17
|
+
Okta, Authentik, Zitadel, Cognito, …). The dedicated modes above add first-class
|
|
18
|
+
support for FastMCP's provider catalogue. `entra-*`/`google` read their config
|
|
19
|
+
from settings (env); the spec-driven modes read it from the profile's
|
|
20
|
+
`auth.inbound.config`, with **secrets referenced by a `<key>_env` entry** naming
|
|
21
|
+
an env var (never inlined):
|
|
22
|
+
|
|
23
|
+
```jsonc
|
|
24
|
+
"auth": {
|
|
25
|
+
"inbound": {
|
|
26
|
+
"mode": "keycloak", // = AUTH_MODE
|
|
27
|
+
"config": { "realm_url": "${env:KEYCLOAK_REALM_URL}" }
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
// AUTH_MODE=auth0: config: { config_url, client_id, audience, client_secret_env: "AUTH0_SECRET" }
|
|
31
|
+
// AUTH_MODE=github: config: { client_id, client_secret_env: "GH_SECRET" }
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
A non-standard / proprietary IdP (e.g. Zammad-as-OAuth2, opaque tokens) is a
|
|
35
|
+
custom provider registered the same way — see below.
|
|
36
|
+
|
|
37
|
+
## A new tool source
|
|
38
|
+
|
|
39
|
+
```python
|
|
40
|
+
# my_pkg/graphql.py
|
|
41
|
+
class GraphQLToolProvider:
|
|
42
|
+
def __init__(self, cfg): self._cfg = cfg
|
|
43
|
+
async def register(self, mcp, ctx) -> int:
|
|
44
|
+
# add tools to `mcp`; use `ctx.client` / `ctx.request(...)` for upstream calls
|
|
45
|
+
...
|
|
46
|
+
return count
|
|
47
|
+
|
|
48
|
+
def create_provider(cfg): # cfg is the profile's ToolsConfig
|
|
49
|
+
return GraphQLToolProvider(cfg)
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
```toml
|
|
53
|
+
# my_pkg/pyproject.toml
|
|
54
|
+
[project.entry-points."bg_mcpcore.tool_sources"]
|
|
55
|
+
graphql = "my_pkg.graphql:create_provider"
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
A provider implements **`ToolProvider`** (`async register(mcp, ctx) -> int`) to
|
|
59
|
+
add tools to an existing instance, or **`ConstructingToolProvider`**
|
|
60
|
+
(`async construct(*, name, instructions, auth, lifespan, icon_url, website_url, ctx) -> FastMCP`)
|
|
61
|
+
to build the instance itself (as the OpenAPI source does via `from_openapi`).
|
|
62
|
+
|
|
63
|
+
## A new auth provider / resolver
|
|
64
|
+
|
|
65
|
+
```python
|
|
66
|
+
def build_my_idp(settings): ... # -> a FastMCP auth provider (or None)
|
|
67
|
+
def build_my_resolver(cfg): ... # -> an AuthHeaderSource
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
```toml
|
|
71
|
+
[project.entry-points."bg_mcpcore.auth_providers"]
|
|
72
|
+
my-idp = "my_pkg.auth:build_my_idp"
|
|
73
|
+
|
|
74
|
+
[project.entry-points."bg_mcpcore.auth_resolvers"]
|
|
75
|
+
sigv4 = "my_pkg.auth:build_my_resolver"
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
## A reusable registry tool
|
|
79
|
+
|
|
80
|
+
```python
|
|
81
|
+
def register(mcp, ctx) -> None:
|
|
82
|
+
@mcp.tool
|
|
83
|
+
async def my_tool() -> str: ...
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
```toml
|
|
87
|
+
[project.entry-points."bg_mcpcore.tools"]
|
|
88
|
+
"acme.search" = "my_pkg.tools:register"
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
!!! warning "Least privilege"
|
|
92
|
+
The OpenAPI/registry sources receive a capability-scoped `ToolContext`
|
|
93
|
+
(a `client` + logger), not the full secret-bearing settings. Only the
|
|
94
|
+
`python` escape hatch (the server's own trusted code) receives `settings`.
|