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.
Files changed (88) hide show
  1. bg_mcpcore-1.0.0/.gitignore +65 -0
  2. bg_mcpcore-1.0.0/CHANGELOG.md +3 -0
  3. bg_mcpcore-1.0.0/LICENSE +21 -0
  4. bg_mcpcore-1.0.0/PKG-INFO +148 -0
  5. bg_mcpcore-1.0.0/README.MD +88 -0
  6. bg_mcpcore-1.0.0/docs/README.template.MD +85 -0
  7. bg_mcpcore-1.0.0/docs/SECURITY.template.MD +92 -0
  8. bg_mcpcore-1.0.0/docs/index.md +36 -0
  9. bg_mcpcore-1.0.0/docs/plugins.md +94 -0
  10. bg_mcpcore-1.0.0/docs/profiles.md +78 -0
  11. bg_mcpcore-1.0.0/docs/quickstart.md +62 -0
  12. bg_mcpcore-1.0.0/docs/security.md +73 -0
  13. bg_mcpcore-1.0.0/examples/README.md +19 -0
  14. bg_mcpcore-1.0.0/examples/openapi_server/main.py +20 -0
  15. bg_mcpcore-1.0.0/examples/openapi_server/petstore-mini.json +18 -0
  16. bg_mcpcore-1.0.0/examples/openapi_server/profile.json +20 -0
  17. bg_mcpcore-1.0.0/examples/python_tools_server/main.py +22 -0
  18. bg_mcpcore-1.0.0/examples/python_tools_server/my_tools.py +24 -0
  19. bg_mcpcore-1.0.0/examples/python_tools_server/profile.json +7 -0
  20. bg_mcpcore-1.0.0/examples/registry_server/main.py +10 -0
  21. bg_mcpcore-1.0.0/examples/registry_server/profile.json +7 -0
  22. bg_mcpcore-1.0.0/pyproject.toml +206 -0
  23. bg_mcpcore-1.0.0/src/bg_mcpcore/__init__.py +265 -0
  24. bg_mcpcore-1.0.0/src/bg_mcpcore/_config_protocols.py +67 -0
  25. bg_mcpcore-1.0.0/src/bg_mcpcore/app.py +194 -0
  26. bg_mcpcore-1.0.0/src/bg_mcpcore/auth/__init__.py +27 -0
  27. bg_mcpcore-1.0.0/src/bg_mcpcore/auth/generic_oidc.py +134 -0
  28. bg_mcpcore-1.0.0/src/bg_mcpcore/auth/resolvers.py +74 -0
  29. bg_mcpcore-1.0.0/src/bg_mcpcore/auth/storage.py +130 -0
  30. bg_mcpcore-1.0.0/src/bg_mcpcore/cli.py +79 -0
  31. bg_mcpcore-1.0.0/src/bg_mcpcore/extensions/__init__.py +12 -0
  32. bg_mcpcore-1.0.0/src/bg_mcpcore/extensions/config.py +235 -0
  33. bg_mcpcore-1.0.0/src/bg_mcpcore/extensions/loader.py +57 -0
  34. bg_mcpcore-1.0.0/src/bg_mcpcore/extensions/prompts.py +77 -0
  35. bg_mcpcore-1.0.0/src/bg_mcpcore/extensions/resources.py +91 -0
  36. bg_mcpcore-1.0.0/src/bg_mcpcore/http/__init__.py +8 -0
  37. bg_mcpcore-1.0.0/src/bg_mcpcore/http/client.py +117 -0
  38. bg_mcpcore-1.0.0/src/bg_mcpcore/http/retry.py +60 -0
  39. bg_mcpcore-1.0.0/src/bg_mcpcore/observability/__init__.py +19 -0
  40. bg_mcpcore-1.0.0/src/bg_mcpcore/observability/banner.py +56 -0
  41. bg_mcpcore-1.0.0/src/bg_mcpcore/observability/logging_setup.py +170 -0
  42. bg_mcpcore-1.0.0/src/bg_mcpcore/observability/sentry.py +51 -0
  43. bg_mcpcore-1.0.0/src/bg_mcpcore/openapi/__init__.py +15 -0
  44. bg_mcpcore-1.0.0/src/bg_mcpcore/openapi/loader.py +274 -0
  45. bg_mcpcore-1.0.0/src/bg_mcpcore/openapi/tool_provider.py +218 -0
  46. bg_mcpcore-1.0.0/src/bg_mcpcore/plugins.py +207 -0
  47. bg_mcpcore-1.0.0/src/bg_mcpcore/profile/__init__.py +26 -0
  48. bg_mcpcore-1.0.0/src/bg_mcpcore/profile/loader.py +96 -0
  49. bg_mcpcore-1.0.0/src/bg_mcpcore/profile/models.py +110 -0
  50. bg_mcpcore-1.0.0/src/bg_mcpcore/profile/schema.json +295 -0
  51. bg_mcpcore-1.0.0/src/bg_mcpcore/providers/__init__.py +21 -0
  52. bg_mcpcore-1.0.0/src/bg_mcpcore/providers/entra.py +87 -0
  53. bg_mcpcore-1.0.0/src/bg_mcpcore/providers/generic.py +196 -0
  54. bg_mcpcore-1.0.0/src/bg_mcpcore/providers/google.py +46 -0
  55. bg_mcpcore-1.0.0/src/bg_mcpcore/providers/middleware.py +79 -0
  56. bg_mcpcore-1.0.0/src/bg_mcpcore/providers/protocols.py +32 -0
  57. bg_mcpcore-1.0.0/src/bg_mcpcore/py.typed +0 -0
  58. bg_mcpcore-1.0.0/src/bg_mcpcore/server/__init__.py +21 -0
  59. bg_mcpcore-1.0.0/src/bg_mcpcore/server/middleware.py +131 -0
  60. bg_mcpcore-1.0.0/src/bg_mcpcore/server/routes.py +89 -0
  61. bg_mcpcore-1.0.0/src/bg_mcpcore/server/runner.py +60 -0
  62. bg_mcpcore-1.0.0/src/bg_mcpcore/settings/__init__.py +35 -0
  63. bg_mcpcore-1.0.0/src/bg_mcpcore/settings/base.py +74 -0
  64. bg_mcpcore-1.0.0/src/bg_mcpcore/settings/enums.py +14 -0
  65. bg_mcpcore-1.0.0/src/bg_mcpcore/settings/factory.py +29 -0
  66. bg_mcpcore-1.0.0/src/bg_mcpcore/settings/helpers.py +86 -0
  67. bg_mcpcore-1.0.0/src/bg_mcpcore/settings/mixins.py +111 -0
  68. bg_mcpcore-1.0.0/src/bg_mcpcore/testing/__init__.py +7 -0
  69. bg_mcpcore-1.0.0/src/bg_mcpcore/testing/plugin.py +49 -0
  70. bg_mcpcore-1.0.0/src/bg_mcpcore/testing/stubs.py +36 -0
  71. bg_mcpcore-1.0.0/src/bg_mcpcore/tools/__init__.py +16 -0
  72. bg_mcpcore-1.0.0/src/bg_mcpcore/tools/protocol.py +71 -0
  73. bg_mcpcore-1.0.0/src/bg_mcpcore/tools/registry.py +93 -0
  74. bg_mcpcore-1.0.0/tests/__init__.py +0 -0
  75. bg_mcpcore-1.0.0/tests/test_assembler.py +52 -0
  76. bg_mcpcore-1.0.0/tests/test_auth_coverage.py +107 -0
  77. bg_mcpcore-1.0.0/tests/test_extensions.py +90 -0
  78. bg_mcpcore-1.0.0/tests/test_extras.py +52 -0
  79. bg_mcpcore-1.0.0/tests/test_logging.py +45 -0
  80. bg_mcpcore-1.0.0/tests/test_openapi.py +66 -0
  81. bg_mcpcore-1.0.0/tests/test_profile.py +115 -0
  82. bg_mcpcore-1.0.0/tests/test_providers.py +94 -0
  83. bg_mcpcore-1.0.0/tests/test_providers_generic.py +66 -0
  84. bg_mcpcore-1.0.0/tests/test_rate_limit.py +103 -0
  85. bg_mcpcore-1.0.0/tests/test_routes.py +39 -0
  86. bg_mcpcore-1.0.0/tests/test_settings.py +104 -0
  87. bg_mcpcore-1.0.0/tests/test_smoke.py +23 -0
  88. 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/
@@ -0,0 +1,3 @@
1
+ # CHANGELOG
2
+
3
+ <!-- This file is managed by python-semantic-release. Do not hand-edit below this line. -->
@@ -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
+ [![PyPI Version](https://badge.fury.io/py/bg-mcpcore.svg)](https://pypi.org/project/bg-mcpcore/)
67
+ [![Python Support](https://img.shields.io/pypi/pyversions/bg-mcpcore.svg)](https://pypi.org/project/bg-mcpcore/)
68
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
69
+ [![🧪 Tests](https://github.com/bauer-group/LIB-BG-MCPCore/actions/workflows/tests.yml/badge.svg)](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
+ [![PyPI Version](https://badge.fury.io/py/bg-mcpcore.svg)](https://pypi.org/project/bg-mcpcore/)
7
+ [![Python Support](https://img.shields.io/pypi/pyversions/bg-mcpcore.svg)](https://pypi.org/project/bg-mcpcore/)
8
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
9
+ [![🧪 Tests](https://github.com/bauer-group/LIB-BG-MCPCore/actions/workflows/tests.yml/badge.svg)](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
+ [![PyPI Version](https://badge.fury.io/py/bg-mcpcore.svg)](https://pypi.org/project/bg-mcpcore/)
4
+ [![Python Support](https://img.shields.io/pypi/pyversions/bg-mcpcore.svg)](https://pypi.org/project/bg-mcpcore/)
5
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
6
+ [![🧪 Tests](https://github.com/bauer-group/LIB-BG-MCPCore/actions/workflows/tests.yml/badge.svg)](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`.