imbi-mcp 0.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.
- imbi_mcp-0.0.0/.github/workflows/docker.yml +95 -0
- imbi_mcp-0.0.0/.github/workflows/publish.yml +45 -0
- imbi_mcp-0.0.0/.github/workflows/test.yml +50 -0
- imbi_mcp-0.0.0/.gitignore +8 -0
- imbi_mcp-0.0.0/.pre-commit-config.yaml +22 -0
- imbi_mcp-0.0.0/.python-version +1 -0
- imbi_mcp-0.0.0/AGENTS.md +75 -0
- imbi_mcp-0.0.0/Dockerfile +24 -0
- imbi_mcp-0.0.0/LICENSE +28 -0
- imbi_mcp-0.0.0/PKG-INFO +89 -0
- imbi_mcp-0.0.0/README.md +75 -0
- imbi_mcp-0.0.0/justfile +51 -0
- imbi_mcp-0.0.0/pyproject.toml +125 -0
- imbi_mcp-0.0.0/src/imbi_mcp/__init__.py +6 -0
- imbi_mcp-0.0.0/src/imbi_mcp/app.py +48 -0
- imbi_mcp-0.0.0/src/imbi_mcp/py.typed +0 -0
- imbi_mcp-0.0.0/src/imbi_mcp/server.py +81 -0
- imbi_mcp-0.0.0/tests/__init__.py +0 -0
- imbi_mcp-0.0.0/tests/helpers.py +39 -0
- imbi_mcp-0.0.0/tests/test_app.py +50 -0
- imbi_mcp-0.0.0/tests/test_server.py +162 -0
- imbi_mcp-0.0.0/uv.lock +1588 -0
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
name: "Build and Publish Docker Image"
|
|
2
|
+
on:
|
|
3
|
+
release:
|
|
4
|
+
types: [published]
|
|
5
|
+
|
|
6
|
+
permissions:
|
|
7
|
+
attestations: write
|
|
8
|
+
contents: write
|
|
9
|
+
id-token: write
|
|
10
|
+
packages: write
|
|
11
|
+
|
|
12
|
+
jobs:
|
|
13
|
+
build-python-package:
|
|
14
|
+
runs-on: ubuntu-latest
|
|
15
|
+
steps:
|
|
16
|
+
- uses: actions/checkout@v5
|
|
17
|
+
|
|
18
|
+
- name: Install uv
|
|
19
|
+
uses: astral-sh/setup-uv@v7
|
|
20
|
+
with:
|
|
21
|
+
enable-cache: true
|
|
22
|
+
|
|
23
|
+
- name: Build python package
|
|
24
|
+
run: uv build
|
|
25
|
+
|
|
26
|
+
- name: Upload build artifacts
|
|
27
|
+
uses: actions/upload-artifact@v6
|
|
28
|
+
with:
|
|
29
|
+
name: python-distributions
|
|
30
|
+
path: dist/*
|
|
31
|
+
|
|
32
|
+
- name: Attach distributions
|
|
33
|
+
env:
|
|
34
|
+
GH_TOKEN: ${{ github.token }}
|
|
35
|
+
RELEASE_TAG: ${{ github.event.release.tag_name }}
|
|
36
|
+
run: |
|
|
37
|
+
tag="$RELEASE_TAG"
|
|
38
|
+
existing=$(gh release view "$tag" --json assets --jq '.assets[].name')
|
|
39
|
+
for file in dist/*.whl dist/*.tar.gz; do
|
|
40
|
+
name=$(basename "$file")
|
|
41
|
+
if echo "$existing" | grep -Fxq "$name"; then
|
|
42
|
+
echo "Asset exists, skipping: $name"
|
|
43
|
+
else
|
|
44
|
+
gh release upload "$tag" "$file"
|
|
45
|
+
fi
|
|
46
|
+
done
|
|
47
|
+
|
|
48
|
+
build-and-publish-docker:
|
|
49
|
+
runs-on: ubuntu-latest
|
|
50
|
+
needs: build-python-package
|
|
51
|
+
steps:
|
|
52
|
+
- uses: actions/checkout@v5
|
|
53
|
+
|
|
54
|
+
- name: Download python distributions
|
|
55
|
+
uses: actions/download-artifact@v6
|
|
56
|
+
with:
|
|
57
|
+
name: python-distributions
|
|
58
|
+
path: dist
|
|
59
|
+
|
|
60
|
+
- uses: docker/setup-qemu-action@v3
|
|
61
|
+
|
|
62
|
+
- uses: docker/setup-buildx-action@v3
|
|
63
|
+
|
|
64
|
+
- name: Log in to ghcr.io
|
|
65
|
+
uses: docker/login-action@v3
|
|
66
|
+
with:
|
|
67
|
+
registry: ghcr.io
|
|
68
|
+
username: ${{ github.actor }}
|
|
69
|
+
password: ${{ secrets.GITHUB_TOKEN }}
|
|
70
|
+
|
|
71
|
+
- id: meta
|
|
72
|
+
uses: docker/metadata-action@v5
|
|
73
|
+
with:
|
|
74
|
+
images: ghcr.io/${{ github.repository }}
|
|
75
|
+
tags: |
|
|
76
|
+
type=semver,pattern={{version}}
|
|
77
|
+
type=semver,pattern={{major}}.{{minor}}
|
|
78
|
+
type=semver,pattern={{major}}
|
|
79
|
+
|
|
80
|
+
- id: build
|
|
81
|
+
uses: docker/build-push-action@v6
|
|
82
|
+
with:
|
|
83
|
+
context: .
|
|
84
|
+
platforms: linux/amd64,linux/arm64
|
|
85
|
+
push: true
|
|
86
|
+
tags: ${{ steps.meta.outputs.tags }}
|
|
87
|
+
labels: ${{ steps.meta.outputs.labels }}
|
|
88
|
+
cache-from: type=gha
|
|
89
|
+
cache-to: type=gha,mode=max
|
|
90
|
+
|
|
91
|
+
- uses: actions/attest-build-provenance@v3
|
|
92
|
+
with:
|
|
93
|
+
subject-name: ghcr.io/${{ github.repository }}
|
|
94
|
+
subject-digest: ${{ steps.build.outputs.digest }}
|
|
95
|
+
push-to-registry: true
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
name: Publish to PyPI
|
|
2
|
+
on:
|
|
3
|
+
release:
|
|
4
|
+
types: [published]
|
|
5
|
+
|
|
6
|
+
permissions:
|
|
7
|
+
id-token: write
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
build:
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
steps:
|
|
13
|
+
- name: Checkout repository
|
|
14
|
+
uses: actions/checkout@v6
|
|
15
|
+
with:
|
|
16
|
+
fetch-depth: 0
|
|
17
|
+
|
|
18
|
+
- name: Install uv
|
|
19
|
+
uses: astral-sh/setup-uv@v5
|
|
20
|
+
|
|
21
|
+
- name: Set up Python
|
|
22
|
+
run: uv python install 3.14
|
|
23
|
+
|
|
24
|
+
- name: Build
|
|
25
|
+
run: uv build
|
|
26
|
+
|
|
27
|
+
- name: Upload dist
|
|
28
|
+
uses: actions/upload-artifact@v4
|
|
29
|
+
with:
|
|
30
|
+
name: dist
|
|
31
|
+
path: dist/
|
|
32
|
+
|
|
33
|
+
publish:
|
|
34
|
+
needs: build
|
|
35
|
+
runs-on: ubuntu-latest
|
|
36
|
+
environment: pypi
|
|
37
|
+
steps:
|
|
38
|
+
- name: Download dist
|
|
39
|
+
uses: actions/download-artifact@v4
|
|
40
|
+
with:
|
|
41
|
+
name: dist
|
|
42
|
+
path: dist/
|
|
43
|
+
|
|
44
|
+
- name: Publish package
|
|
45
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
name: "Run Tests"
|
|
2
|
+
on:
|
|
3
|
+
push:
|
|
4
|
+
branches: [ main ]
|
|
5
|
+
pull_request:
|
|
6
|
+
branches: [ main ]
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
lint:
|
|
10
|
+
name: "Static Analysis"
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
steps:
|
|
13
|
+
- name: Checkout repository
|
|
14
|
+
uses: actions/checkout@v5
|
|
15
|
+
- name: Install just
|
|
16
|
+
uses: extractions/setup-just@v2
|
|
17
|
+
- name: Install uv
|
|
18
|
+
uses: astral-sh/setup-uv@v7
|
|
19
|
+
with:
|
|
20
|
+
enable-cache: true
|
|
21
|
+
- name: Run lint checks
|
|
22
|
+
run: just lint
|
|
23
|
+
|
|
24
|
+
test:
|
|
25
|
+
name: "Automated Tests"
|
|
26
|
+
runs-on: ubuntu-latest
|
|
27
|
+
strategy:
|
|
28
|
+
fail-fast: false
|
|
29
|
+
matrix:
|
|
30
|
+
python: [ "3.14" ]
|
|
31
|
+
steps:
|
|
32
|
+
- name: Checkout repository
|
|
33
|
+
uses: actions/checkout@v5
|
|
34
|
+
- name: Install just
|
|
35
|
+
uses: extractions/setup-just@v2
|
|
36
|
+
- name: Install uv
|
|
37
|
+
uses: astral-sh/setup-uv@v7
|
|
38
|
+
with:
|
|
39
|
+
enable-cache: true
|
|
40
|
+
python-version: ${{ matrix.python }}
|
|
41
|
+
- name: Run tests
|
|
42
|
+
env:
|
|
43
|
+
UV_FROZEN: "1"
|
|
44
|
+
run: just test
|
|
45
|
+
- name: Upload coverage reports
|
|
46
|
+
uses: codecov/codecov-action@v5
|
|
47
|
+
with:
|
|
48
|
+
token: ${{ secrets.CODECOV_TOKEN }}
|
|
49
|
+
files: "build/coverage.xml"
|
|
50
|
+
flags: unittests
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
repos:
|
|
2
|
+
- repo: https://github.com/pre-commit/pre-commit-hooks
|
|
3
|
+
rev: v6.0.0 # unrelated to the pre-commit utility version
|
|
4
|
+
hooks:
|
|
5
|
+
- id: check-executables-have-shebangs
|
|
6
|
+
- id: check-json
|
|
7
|
+
- id: check-shebang-scripts-are-executable
|
|
8
|
+
- id: check-toml
|
|
9
|
+
- id: check-yaml
|
|
10
|
+
- id: end-of-file-fixer
|
|
11
|
+
- id: mixed-line-ending
|
|
12
|
+
- id: trailing-whitespace
|
|
13
|
+
- repo: https://github.com/astral-sh/ruff-pre-commit
|
|
14
|
+
rev: v0.15.0
|
|
15
|
+
hooks:
|
|
16
|
+
- id: ruff-check
|
|
17
|
+
args: [--fix]
|
|
18
|
+
- id: ruff-format
|
|
19
|
+
- repo: https://github.com/tombi-toml/tombi-pre-commit
|
|
20
|
+
rev: v0.7.27
|
|
21
|
+
hooks:
|
|
22
|
+
- id: tombi-format
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
3.14
|
imbi_mcp-0.0.0/AGENTS.md
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
# Agent Instructions for imbi-mcp
|
|
2
|
+
|
|
3
|
+
## Project Overview
|
|
4
|
+
|
|
5
|
+
imbi-mcp is an MCP (Model Context Protocol) server that auto-generates tools
|
|
6
|
+
and resources from the imbi-api OpenAPI specification. It acts as a bridge
|
|
7
|
+
between AI agents and the Imbi DevOps service management platform.
|
|
8
|
+
|
|
9
|
+
## Architecture
|
|
10
|
+
|
|
11
|
+
```
|
|
12
|
+
AI Agent ──MCP──► imbi-mcp ──HTTP──► imbi-api ──► Neo4j / ClickHouse
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
- `src/imbi_mcp/server.py` -- Core server logic. Fetches the OpenAPI spec
|
|
16
|
+
from imbi-api at startup and generates MCP tools/resources via FastMCP.
|
|
17
|
+
Route maps control which endpoints are exposed and how they are classified.
|
|
18
|
+
- `src/imbi_mcp/app.py` -- Typer CLI entry point. The `serve` command
|
|
19
|
+
creates the server and starts it on the configured transport.
|
|
20
|
+
- `src/imbi_mcp/__init__.py` -- Package version from installed metadata.
|
|
21
|
+
|
|
22
|
+
## Key Design Decisions
|
|
23
|
+
|
|
24
|
+
- The server requires a running imbi-api instance at startup to fetch the
|
|
25
|
+
OpenAPI spec. If the API is unreachable, startup fails with a clear error.
|
|
26
|
+
- Auth is forwarded transparently: the MCP caller's `Authorization` header
|
|
27
|
+
is injected into every request to imbi-api via an httpx event hook.
|
|
28
|
+
- Route maps are evaluated in order (first match wins). Exclusions come
|
|
29
|
+
before semantic mappings.
|
|
30
|
+
|
|
31
|
+
## Code Standards
|
|
32
|
+
|
|
33
|
+
- **Line length**: 79 characters
|
|
34
|
+
- **Quotes**: Single quotes for strings, double quotes for docstrings
|
|
35
|
+
- **Type checking**: basedpyright strict mode + mypy strict mode
|
|
36
|
+
- **Testing**: unittest, 90% minimum coverage
|
|
37
|
+
- **Imports**: Prefer module imports (`from unittest import mock`)
|
|
38
|
+
over object imports (`from unittest.mock import patch`)
|
|
39
|
+
- **Logging**: Use `%s` formatting, not f-strings
|
|
40
|
+
- **Async**: All I/O in production code must be async
|
|
41
|
+
|
|
42
|
+
## Development Commands
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
just setup # Install dependencies and pre-commit hooks
|
|
46
|
+
just test # Run pytest with coverage
|
|
47
|
+
just lint # Run pre-commit, basedpyright, mypy
|
|
48
|
+
just format # Auto-format with ruff and tombi
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## Testing
|
|
52
|
+
|
|
53
|
+
Tests use `unittest.TestCase` and `unittest.IsolatedAsyncioTestCase`.
|
|
54
|
+
All external I/O (httpx calls, FastMCP context) is mocked.
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
uv run pytest # Run all tests
|
|
58
|
+
uv run pytest tests/test_server.py # Run specific test file
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## CI/CD
|
|
62
|
+
|
|
63
|
+
GitHub Actions runs on Python 3.12, 3.13, and 3.14:
|
|
64
|
+
- **Static Analysis**: pre-commit hooks + basedpyright
|
|
65
|
+
- **Automated Tests**: pytest with coverage reporting
|
|
66
|
+
|
|
67
|
+
## Dependencies
|
|
68
|
+
|
|
69
|
+
- **fastmcp** -- MCP server framework with OpenAPI auto-generation
|
|
70
|
+
- **httpx** -- Async HTTP client for API communication
|
|
71
|
+
- **imbi-common** -- Shared models and utilities (installed from git)
|
|
72
|
+
- **typer** -- CLI framework (via fastmcp dependency)
|
|
73
|
+
|
|
74
|
+
Manage dependencies with `uv`. After changing `pyproject.toml`, run
|
|
75
|
+
`uv lock` to update the lock file.
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
FROM python:3.14-slim AS builder
|
|
2
|
+
|
|
3
|
+
WORKDIR /tmp/install
|
|
4
|
+
|
|
5
|
+
RUN pip install uv \
|
|
6
|
+
&& apt update \
|
|
7
|
+
&& apt install git --yes
|
|
8
|
+
|
|
9
|
+
COPY dist/*.whl pyproject.toml uv.lock /tmp/install/
|
|
10
|
+
|
|
11
|
+
RUN uv venv /app \
|
|
12
|
+
&& . /app/bin/activate \
|
|
13
|
+
&& uv sync --no-dev --active --frozen --no-install-project \
|
|
14
|
+
&& uv pip install *.whl
|
|
15
|
+
|
|
16
|
+
FROM python:3.14-slim AS service
|
|
17
|
+
EXPOSE 8001
|
|
18
|
+
ENV PATH="/app/bin:$PATH"
|
|
19
|
+
WORKDIR /app
|
|
20
|
+
COPY --from=builder /app/ /app/
|
|
21
|
+
RUN useradd -r -g users imbi \
|
|
22
|
+
&& chown -R imbi /app
|
|
23
|
+
USER imbi
|
|
24
|
+
CMD ["/app/bin/imbi-mcp", "serve", "--transport", "streamable-http", "--host", "0.0.0.0"]
|
imbi_mcp-0.0.0/LICENSE
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
BSD 3-Clause License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025, Gavin M. Roy
|
|
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
|
+
3. Neither the name of the copyright holder nor the names of its
|
|
16
|
+
contributors may be used to endorse or promote products derived from
|
|
17
|
+
this software without specific prior written permission.
|
|
18
|
+
|
|
19
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
20
|
+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
21
|
+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
22
|
+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
23
|
+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
24
|
+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
25
|
+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
26
|
+
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
27
|
+
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
28
|
+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
imbi_mcp-0.0.0/PKG-INFO
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: imbi-mcp
|
|
3
|
+
Version: 0.0.0
|
|
4
|
+
Summary: Imbi MCP server
|
|
5
|
+
Author-email: "Gavin M. Roy" <gavinr@aweber.com>
|
|
6
|
+
License-Expression: BSD-3-Clause
|
|
7
|
+
License-File: LICENSE
|
|
8
|
+
Requires-Python: >=3.14
|
|
9
|
+
Requires-Dist: fastmcp>=2.0.0
|
|
10
|
+
Requires-Dist: httpx>=0.28.1
|
|
11
|
+
Requires-Dist: imbi-common
|
|
12
|
+
Requires-Dist: pydantic~=2.12
|
|
13
|
+
Description-Content-Type: text/markdown
|
|
14
|
+
|
|
15
|
+
# imbi-mcp
|
|
16
|
+
|
|
17
|
+
MCP server for the [Imbi](https://github.com/AWeber-Imbi/imbi) DevOps service
|
|
18
|
+
management platform. Exposes Imbi API functionality to AI agents via the
|
|
19
|
+
[Model Context Protocol](https://modelcontextprotocol.io/).
|
|
20
|
+
|
|
21
|
+
## How It Works
|
|
22
|
+
|
|
23
|
+
At startup the server fetches the OpenAPI spec from a running
|
|
24
|
+
[imbi-api](https://github.com/AWeber-Imbi/imbi-api) instance and
|
|
25
|
+
auto-generates MCP tools, resources, and resource templates using
|
|
26
|
+
[FastMCP](https://github.com/jlowin/fastmcp).
|
|
27
|
+
|
|
28
|
+
Route mapping rules control what gets exposed:
|
|
29
|
+
|
|
30
|
+
- **Excluded** -- Auth, MFA, status, and thumbnail endpoints are hidden.
|
|
31
|
+
- **Resources** -- `GET` endpoints that return collections.
|
|
32
|
+
- **Resource templates** -- `GET` endpoints with path parameters.
|
|
33
|
+
- **Tools** -- Everything else (create, update, delete operations).
|
|
34
|
+
|
|
35
|
+
The caller's `Authorization` header is forwarded to the API so that
|
|
36
|
+
requests run with the caller's permissions.
|
|
37
|
+
|
|
38
|
+
## Requirements
|
|
39
|
+
|
|
40
|
+
- Python 3.12+
|
|
41
|
+
- A running imbi-api instance
|
|
42
|
+
|
|
43
|
+
## Quick Start
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
# Install dependencies
|
|
47
|
+
just setup
|
|
48
|
+
|
|
49
|
+
# Run the server (imbi-api must be reachable)
|
|
50
|
+
just serve
|
|
51
|
+
|
|
52
|
+
# Or with explicit options
|
|
53
|
+
imbi-mcp serve --api-url http://localhost:8000 --transport streamable-http
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## CLI Options
|
|
57
|
+
|
|
58
|
+
```
|
|
59
|
+
imbi-mcp serve [OPTIONS]
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
| Option | Default | Env Var | Description |
|
|
63
|
+
| --------------- | ------------------------ | -------------- | ------------------------ |
|
|
64
|
+
| `--api-url` | `http://localhost:8000` | `IMBI_INTERNAL_API_URL` | Base URL of the Imbi API |
|
|
65
|
+
| `--transport` | `streamable-http` | | MCP transport type |
|
|
66
|
+
| `--host` | `127.0.0.1` | | Host to bind to |
|
|
67
|
+
| `--port` | `8001` | | Port to bind to |
|
|
68
|
+
|
|
69
|
+
Supported transports: `stdio`, `http`, `sse`, `streamable-http`
|
|
70
|
+
|
|
71
|
+
## Docker
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
docker build -t imbi-mcp .
|
|
75
|
+
docker run -p 8001:8001 -e IMBI_INTERNAL_API_URL=http://imbi-api:8000 imbi-mcp
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
## Development
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
just setup # Install deps and pre-commit hooks
|
|
82
|
+
just test # Run tests (90% coverage minimum)
|
|
83
|
+
just lint # Run ruff, basedpyright, mypy
|
|
84
|
+
just format # Auto-format code
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
## License
|
|
88
|
+
|
|
89
|
+
BSD-3-Clause
|
imbi_mcp-0.0.0/README.md
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
# imbi-mcp
|
|
2
|
+
|
|
3
|
+
MCP server for the [Imbi](https://github.com/AWeber-Imbi/imbi) DevOps service
|
|
4
|
+
management platform. Exposes Imbi API functionality to AI agents via the
|
|
5
|
+
[Model Context Protocol](https://modelcontextprotocol.io/).
|
|
6
|
+
|
|
7
|
+
## How It Works
|
|
8
|
+
|
|
9
|
+
At startup the server fetches the OpenAPI spec from a running
|
|
10
|
+
[imbi-api](https://github.com/AWeber-Imbi/imbi-api) instance and
|
|
11
|
+
auto-generates MCP tools, resources, and resource templates using
|
|
12
|
+
[FastMCP](https://github.com/jlowin/fastmcp).
|
|
13
|
+
|
|
14
|
+
Route mapping rules control what gets exposed:
|
|
15
|
+
|
|
16
|
+
- **Excluded** -- Auth, MFA, status, and thumbnail endpoints are hidden.
|
|
17
|
+
- **Resources** -- `GET` endpoints that return collections.
|
|
18
|
+
- **Resource templates** -- `GET` endpoints with path parameters.
|
|
19
|
+
- **Tools** -- Everything else (create, update, delete operations).
|
|
20
|
+
|
|
21
|
+
The caller's `Authorization` header is forwarded to the API so that
|
|
22
|
+
requests run with the caller's permissions.
|
|
23
|
+
|
|
24
|
+
## Requirements
|
|
25
|
+
|
|
26
|
+
- Python 3.12+
|
|
27
|
+
- A running imbi-api instance
|
|
28
|
+
|
|
29
|
+
## Quick Start
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
# Install dependencies
|
|
33
|
+
just setup
|
|
34
|
+
|
|
35
|
+
# Run the server (imbi-api must be reachable)
|
|
36
|
+
just serve
|
|
37
|
+
|
|
38
|
+
# Or with explicit options
|
|
39
|
+
imbi-mcp serve --api-url http://localhost:8000 --transport streamable-http
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## CLI Options
|
|
43
|
+
|
|
44
|
+
```
|
|
45
|
+
imbi-mcp serve [OPTIONS]
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
| Option | Default | Env Var | Description |
|
|
49
|
+
| --------------- | ------------------------ | -------------- | ------------------------ |
|
|
50
|
+
| `--api-url` | `http://localhost:8000` | `IMBI_INTERNAL_API_URL` | Base URL of the Imbi API |
|
|
51
|
+
| `--transport` | `streamable-http` | | MCP transport type |
|
|
52
|
+
| `--host` | `127.0.0.1` | | Host to bind to |
|
|
53
|
+
| `--port` | `8001` | | Port to bind to |
|
|
54
|
+
|
|
55
|
+
Supported transports: `stdio`, `http`, `sse`, `streamable-http`
|
|
56
|
+
|
|
57
|
+
## Docker
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
docker build -t imbi-mcp .
|
|
61
|
+
docker run -p 8001:8001 -e IMBI_INTERNAL_API_URL=http://imbi-api:8000 imbi-mcp
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## Development
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
just setup # Install deps and pre-commit hooks
|
|
68
|
+
just test # Run tests (90% coverage minimum)
|
|
69
|
+
just lint # Run ruff, basedpyright, mypy
|
|
70
|
+
just format # Auto-format code
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
## License
|
|
74
|
+
|
|
75
|
+
BSD-3-Clause
|
imbi_mcp-0.0.0/justfile
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
[doc("Bootstrap the environment and run the MCP server")]
|
|
2
|
+
[group("Testing")]
|
|
3
|
+
serve *ARGS: setup
|
|
4
|
+
-uv run --env-file=.env imbi-mcp serve {{ ARGS }}
|
|
5
|
+
|
|
6
|
+
[default]
|
|
7
|
+
[private]
|
|
8
|
+
ci: lint test
|
|
9
|
+
|
|
10
|
+
[doc("Set up your development environment")]
|
|
11
|
+
[group("Environment")]
|
|
12
|
+
setup:
|
|
13
|
+
uv sync --all-groups --all-extras --frozen
|
|
14
|
+
uv run pre-commit install --install-hooks --overwrite
|
|
15
|
+
|
|
16
|
+
[doc("Run tests")]
|
|
17
|
+
[group("Testing")]
|
|
18
|
+
test: setup
|
|
19
|
+
uv run pytest
|
|
20
|
+
|
|
21
|
+
[doc("Run linters")]
|
|
22
|
+
[group("Testing")]
|
|
23
|
+
lint: setup
|
|
24
|
+
uv run pre-commit run --all-files
|
|
25
|
+
uv run basedpyright
|
|
26
|
+
uv run mypy
|
|
27
|
+
|
|
28
|
+
[doc("Reformat code (optionally pass specific files)")]
|
|
29
|
+
[group("Development")]
|
|
30
|
+
format *FILES: setup
|
|
31
|
+
#!/usr/bin/env sh
|
|
32
|
+
set -x
|
|
33
|
+
if [ "{{FILES}}" = '' ]; then
|
|
34
|
+
args='--all-files'
|
|
35
|
+
else
|
|
36
|
+
args='--files {{FILES}}'
|
|
37
|
+
fi
|
|
38
|
+
uv run pre-commit run ruff-format $args
|
|
39
|
+
uv run pre-commit run tombi-format $args
|
|
40
|
+
|
|
41
|
+
[doc("Remove runtime artifacts")]
|
|
42
|
+
[group("Environment")]
|
|
43
|
+
clean:
|
|
44
|
+
rm -f .coverage
|
|
45
|
+
rm -fR build
|
|
46
|
+
|
|
47
|
+
[confirm]
|
|
48
|
+
[doc("Remove caches, virtual env, and output files")]
|
|
49
|
+
[group("Environment")]
|
|
50
|
+
real-clean: clean
|
|
51
|
+
rm -fR .venv .*_cache dist
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "imbi-mcp"
|
|
7
|
+
version = "0.0.0"
|
|
8
|
+
description = "Imbi MCP server"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.14"
|
|
11
|
+
license = "BSD-3-Clause"
|
|
12
|
+
authors = [{ name = "Gavin M. Roy", email = "gavinr@aweber.com" }]
|
|
13
|
+
dependencies = [
|
|
14
|
+
"fastmcp>=2.0.0",
|
|
15
|
+
"httpx>=0.28.1",
|
|
16
|
+
"imbi-common",
|
|
17
|
+
"pydantic~=2.12",
|
|
18
|
+
]
|
|
19
|
+
|
|
20
|
+
[project.scripts]
|
|
21
|
+
imbi-mcp = "imbi_mcp.app:cli"
|
|
22
|
+
|
|
23
|
+
[dependency-groups]
|
|
24
|
+
dev = [
|
|
25
|
+
"basedpyright>=1.37.1",
|
|
26
|
+
"coverage[toml]>=7.13.1",
|
|
27
|
+
"mypy~=1.19.1",
|
|
28
|
+
"pre-commit>=4.5.1",
|
|
29
|
+
"pytest>=9.0.2",
|
|
30
|
+
"pytest-cov>=7.0.0",
|
|
31
|
+
"python-dotenv>=1.1.0",
|
|
32
|
+
"ruff~=0.14.6",
|
|
33
|
+
]
|
|
34
|
+
|
|
35
|
+
[tool.coverage.report]
|
|
36
|
+
fail_under = 90
|
|
37
|
+
show_missing = true
|
|
38
|
+
|
|
39
|
+
[tool.coverage.run]
|
|
40
|
+
branch = true
|
|
41
|
+
source = ["src"]
|
|
42
|
+
|
|
43
|
+
[tool.mypy]
|
|
44
|
+
namespace_packages = true
|
|
45
|
+
show_error_codes = true
|
|
46
|
+
strict = true
|
|
47
|
+
packages = ["imbi_mcp", "tests"]
|
|
48
|
+
|
|
49
|
+
[tool.pyright]
|
|
50
|
+
deprecateTypingAliases = true
|
|
51
|
+
reportMissingSuperCall = "hint"
|
|
52
|
+
typeCheckingMode = "strict"
|
|
53
|
+
|
|
54
|
+
[[tool.pyright.executionEnvironments]]
|
|
55
|
+
root = "src"
|
|
56
|
+
reportUnusedFunction = "warning"
|
|
57
|
+
|
|
58
|
+
[[tool.pyright.executionEnvironments]]
|
|
59
|
+
root = "tests"
|
|
60
|
+
reportPrivateUsage = "none"
|
|
61
|
+
reportUnusedFunction = "none"
|
|
62
|
+
extraPaths = ["src", "."]
|
|
63
|
+
|
|
64
|
+
[tool.ruff]
|
|
65
|
+
line-length = 79
|
|
66
|
+
target-version = "py314"
|
|
67
|
+
|
|
68
|
+
[tool.ruff.format]
|
|
69
|
+
quote-style = "single"
|
|
70
|
+
|
|
71
|
+
[tool.ruff.lint]
|
|
72
|
+
select = [
|
|
73
|
+
"A",
|
|
74
|
+
"ANN",
|
|
75
|
+
"ARG",
|
|
76
|
+
"ASYNC",
|
|
77
|
+
"B",
|
|
78
|
+
"BLE",
|
|
79
|
+
"C4",
|
|
80
|
+
"C90",
|
|
81
|
+
"DTZ",
|
|
82
|
+
"E",
|
|
83
|
+
"ERA",
|
|
84
|
+
"F",
|
|
85
|
+
"FLY",
|
|
86
|
+
"FURB",
|
|
87
|
+
"G",
|
|
88
|
+
"I",
|
|
89
|
+
"LOG",
|
|
90
|
+
"N",
|
|
91
|
+
"PIE",
|
|
92
|
+
"PL",
|
|
93
|
+
"PTH",
|
|
94
|
+
"Q",
|
|
95
|
+
"RET",
|
|
96
|
+
"RSE",
|
|
97
|
+
"RUF",
|
|
98
|
+
"S",
|
|
99
|
+
"SIM",
|
|
100
|
+
"T10",
|
|
101
|
+
"T20",
|
|
102
|
+
"TC",
|
|
103
|
+
"TRY",
|
|
104
|
+
"W",
|
|
105
|
+
]
|
|
106
|
+
ignore = [
|
|
107
|
+
"TRY003",
|
|
108
|
+
"TRY400",
|
|
109
|
+
]
|
|
110
|
+
flake8-quotes = { inline-quotes = "single" }
|
|
111
|
+
|
|
112
|
+
[tool.pytest.ini_options]
|
|
113
|
+
testpaths = ["tests"]
|
|
114
|
+
addopts = [
|
|
115
|
+
"--cov=src/imbi_mcp",
|
|
116
|
+
"--cov-report=xml:build/coverage.xml",
|
|
117
|
+
"--cov-report=term",
|
|
118
|
+
]
|
|
119
|
+
|
|
120
|
+
[[tool.uv.index]]
|
|
121
|
+
url = "https://pypi.org/simple"
|
|
122
|
+
default = true
|
|
123
|
+
|
|
124
|
+
[tool.uv.sources]
|
|
125
|
+
imbi-common = { git = "https://github.com/AWeber-Imbi/imbi-common", rev = "main" }
|