mcp-openapix 0.1.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.
- mcp_openapix-0.1.0/.gitattributes +4 -0
- mcp_openapix-0.1.0/.github/workflows/ci.yml +84 -0
- mcp_openapix-0.1.0/.gitignore +41 -0
- mcp_openapix-0.1.0/.pre-commit-config.yaml +19 -0
- mcp_openapix-0.1.0/.python-version +1 -0
- mcp_openapix-0.1.0/AGENTS.md +138 -0
- mcp_openapix-0.1.0/CLAUDE.md +1 -0
- mcp_openapix-0.1.0/LICENSE +21 -0
- mcp_openapix-0.1.0/PKG-INFO +284 -0
- mcp_openapix-0.1.0/README.md +246 -0
- mcp_openapix-0.1.0/config.example.json +86 -0
- mcp_openapix-0.1.0/docs/token-protocol.md +208 -0
- mcp_openapix-0.1.0/pyproject.toml +95 -0
- mcp_openapix-0.1.0/server.json +23 -0
- mcp_openapix-0.1.0/src/mcp_openapix/__init__.py +0 -0
- mcp_openapix-0.1.0/src/mcp_openapix/api_client.py +228 -0
- mcp_openapix-0.1.0/src/mcp_openapix/auth.py +568 -0
- mcp_openapix-0.1.0/src/mcp_openapix/cache.py +149 -0
- mcp_openapix-0.1.0/src/mcp_openapix/config.py +496 -0
- mcp_openapix-0.1.0/src/mcp_openapix/refresh.py +305 -0
- mcp_openapix-0.1.0/src/mcp_openapix/responses.py +58 -0
- mcp_openapix-0.1.0/src/mcp_openapix/server.py +540 -0
- mcp_openapix-0.1.0/src/mcp_openapix/spec_loader.py +249 -0
- mcp_openapix-0.1.0/src/mcp_openapix/tools/__init__.py +1 -0
- mcp_openapix-0.1.0/src/mcp_openapix/tools/call_endpoint.py +103 -0
- mcp_openapix-0.1.0/src/mcp_openapix/tools/describe_endpoint.py +43 -0
- mcp_openapix-0.1.0/src/mcp_openapix/tools/list_endpoints.py +67 -0
- mcp_openapix-0.1.0/src/mcp_openapix/tools/list_platforms.py +42 -0
- mcp_openapix-0.1.0/tests/__init__.py +0 -0
- mcp_openapix-0.1.0/tests/conftest.py +188 -0
- mcp_openapix-0.1.0/tests/test_api_client.py +291 -0
- mcp_openapix-0.1.0/tests/test_auth.py +306 -0
- mcp_openapix-0.1.0/tests/test_config.py +253 -0
- mcp_openapix-0.1.0/tests/test_refresh.py +239 -0
- mcp_openapix-0.1.0/tests/test_resources_e2e.py +96 -0
- mcp_openapix-0.1.0/tests/test_responses.py +57 -0
- mcp_openapix-0.1.0/tests/test_spec_loader.py +107 -0
- mcp_openapix-0.1.0/tests/test_tools.py +167 -0
- mcp_openapix-0.1.0/uv.lock +1097 -0
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
name: ci
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
tags: ["*"]
|
|
7
|
+
pull_request:
|
|
8
|
+
branches: [main]
|
|
9
|
+
workflow_dispatch:
|
|
10
|
+
|
|
11
|
+
concurrency:
|
|
12
|
+
group: ${{ github.workflow }}-${{ github.ref }}
|
|
13
|
+
cancel-in-progress: true
|
|
14
|
+
|
|
15
|
+
permissions:
|
|
16
|
+
contents: read
|
|
17
|
+
|
|
18
|
+
jobs:
|
|
19
|
+
test:
|
|
20
|
+
runs-on: ubuntu-latest
|
|
21
|
+
|
|
22
|
+
steps:
|
|
23
|
+
- name: Checkout
|
|
24
|
+
uses: actions/checkout@v7
|
|
25
|
+
|
|
26
|
+
- name: Install uv
|
|
27
|
+
uses: astral-sh/setup-uv@ae62891fec2bb8e7d6c99fc78c9fec3a63790f8d # v10.0.0
|
|
28
|
+
with:
|
|
29
|
+
enable-cache: true
|
|
30
|
+
|
|
31
|
+
- name: Install dependencies
|
|
32
|
+
run: uv sync --extra dev
|
|
33
|
+
|
|
34
|
+
- name: Lint and format
|
|
35
|
+
run: |
|
|
36
|
+
uv run ruff check src/ tests/
|
|
37
|
+
uv run ruff format --check src/ tests/
|
|
38
|
+
|
|
39
|
+
- name: Type check
|
|
40
|
+
run: uv run pyright
|
|
41
|
+
|
|
42
|
+
- name: Test
|
|
43
|
+
run: uv run pytest tests/ -v --tb=short
|
|
44
|
+
|
|
45
|
+
publish:
|
|
46
|
+
name: Release
|
|
47
|
+
needs: test
|
|
48
|
+
if: startsWith(github.ref, 'refs/tags/')
|
|
49
|
+
runs-on: ubuntu-latest
|
|
50
|
+
environment:
|
|
51
|
+
name: pypi
|
|
52
|
+
url: https://pypi.org/project/mcp-openapix/${{ github.ref_name }}/
|
|
53
|
+
permissions:
|
|
54
|
+
contents: read
|
|
55
|
+
id-token: write
|
|
56
|
+
steps:
|
|
57
|
+
- name: Checkout
|
|
58
|
+
uses: actions/checkout@v7
|
|
59
|
+
|
|
60
|
+
- name: Install uv
|
|
61
|
+
uses: astral-sh/setup-uv@ae62891fec2bb8e7d6c99fc78c9fec3a63790f8d # v10.0.0
|
|
62
|
+
with:
|
|
63
|
+
enable-cache: true
|
|
64
|
+
|
|
65
|
+
- name: Build Python package distributions
|
|
66
|
+
run: uv build
|
|
67
|
+
|
|
68
|
+
- name: Publish Python package to PyPI
|
|
69
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
70
|
+
|
|
71
|
+
- name: Install MCP Registry publisher
|
|
72
|
+
run: |
|
|
73
|
+
curl -L "https://github.com/modelcontextprotocol/registry/releases/latest/download/mcp-publisher_$(uname -s | tr '[:upper:]' '[:lower:]')_$(uname -m | sed 's/x86_64/amd64/;s/aarch64/arm64/').tar.gz" | tar xz mcp-publisher
|
|
74
|
+
|
|
75
|
+
- name: Update MCP Registry manifest version
|
|
76
|
+
run: |
|
|
77
|
+
jq --arg version "${GITHUB_REF_NAME}" '.version = $version | .packages[].version = $version' server.json > server.tmp
|
|
78
|
+
mv server.tmp server.json
|
|
79
|
+
|
|
80
|
+
- name: Authenticate MCP Registry publisher
|
|
81
|
+
run: ./mcp-publisher login github-oidc
|
|
82
|
+
|
|
83
|
+
- name: Publish server metadata to MCP Registry
|
|
84
|
+
run: ./mcp-publisher publish
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# Project / local
|
|
2
|
+
.env
|
|
3
|
+
.env.local
|
|
4
|
+
.env.*.local
|
|
5
|
+
|
|
6
|
+
# Python
|
|
7
|
+
__pycache__/
|
|
8
|
+
*.py[cod]
|
|
9
|
+
*$py.class
|
|
10
|
+
*.so
|
|
11
|
+
|
|
12
|
+
# Virtual environment (uv)
|
|
13
|
+
.venv/
|
|
14
|
+
env/
|
|
15
|
+
venv/
|
|
16
|
+
|
|
17
|
+
# Build / packaging
|
|
18
|
+
build/
|
|
19
|
+
dist/
|
|
20
|
+
*.egg-info/
|
|
21
|
+
*.egg
|
|
22
|
+
|
|
23
|
+
# Test / coverage (pytest, pytest-cov)
|
|
24
|
+
.pytest_cache/
|
|
25
|
+
.coverage
|
|
26
|
+
.coverage.*
|
|
27
|
+
htmlcov/
|
|
28
|
+
cover/
|
|
29
|
+
|
|
30
|
+
# Ruff
|
|
31
|
+
.ruff_cache/
|
|
32
|
+
|
|
33
|
+
# mypy
|
|
34
|
+
.mypy_cache/
|
|
35
|
+
|
|
36
|
+
# PyPI (credentials)
|
|
37
|
+
.pypirc
|
|
38
|
+
|
|
39
|
+
# AI agent
|
|
40
|
+
*-progress.md
|
|
41
|
+
.claude/settings.local.json
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# See https://pre-commit.com for more
|
|
2
|
+
# Run: pre-commit install
|
|
3
|
+
|
|
4
|
+
repos:
|
|
5
|
+
- repo: https://github.com/astral-sh/ruff-pre-commit
|
|
6
|
+
rev: v0.8.4
|
|
7
|
+
hooks:
|
|
8
|
+
- id: ruff
|
|
9
|
+
args: [--fix]
|
|
10
|
+
- id: ruff-format
|
|
11
|
+
|
|
12
|
+
- repo: local
|
|
13
|
+
hooks:
|
|
14
|
+
- id: pyright
|
|
15
|
+
name: pyright
|
|
16
|
+
entry: uv run pyright
|
|
17
|
+
language: system
|
|
18
|
+
types: [python]
|
|
19
|
+
pass_filenames: false
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
3.13
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
# Agent Instructions
|
|
2
|
+
|
|
3
|
+
Rules AI agents must follow when working in this repository.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Commit messages
|
|
8
|
+
|
|
9
|
+
Use **Conventional Commits**.
|
|
10
|
+
|
|
11
|
+
### Header
|
|
12
|
+
|
|
13
|
+
* Format: `<type>(optional scope): summary`
|
|
14
|
+
* Use lowercase types (`feat`, `fix`, `ci`, `chore`, `docs`)
|
|
15
|
+
* Use scopes when relevant
|
|
16
|
+
* Write summaries in lowercase, imperative mood
|
|
17
|
+
|
|
18
|
+
### Body
|
|
19
|
+
|
|
20
|
+
* Leave a blank line after the header
|
|
21
|
+
* Explain **why**, not what
|
|
22
|
+
* Use imperative, present tense
|
|
23
|
+
* Wrap lines at ~72 characters
|
|
24
|
+
|
|
25
|
+
The body is optional for trivial changes.
|
|
26
|
+
|
|
27
|
+
---
|
|
28
|
+
|
|
29
|
+
## Commits
|
|
30
|
+
|
|
31
|
+
When generating commits via a shell:
|
|
32
|
+
|
|
33
|
+
* Do **not** pass generated messages directly to `git commit -m`
|
|
34
|
+
* Write the commit message to a file or standard input
|
|
35
|
+
* Use `git commit -F <file>` or `git commit -F -`
|
|
36
|
+
* Disable shell expansion when writing commit messages
|
|
37
|
+
|
|
38
|
+
This avoids issues with backticks, quotes, and other shell-expanded characters in
|
|
39
|
+
generated commit messages.
|
|
40
|
+
|
|
41
|
+
---
|
|
42
|
+
|
|
43
|
+
## Attribution
|
|
44
|
+
|
|
45
|
+
Every AI-assisted commit, tag, PR, comment, reply, or message an agent writes for
|
|
46
|
+
someone must carry an `Assisted-by` trailer:
|
|
47
|
+
|
|
48
|
+
```
|
|
49
|
+
Assisted-by: AGENT_NAME:MODEL_VERSION [TOOL1] [TOOL2]
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
| Field | Description |
|
|
53
|
+
|-------------------|-----------------------------------------------------------|
|
|
54
|
+
| `AGENT_NAME` | AI tool or framework (e.g. `Claude`, `Cursor`, `Copilot`) |
|
|
55
|
+
| `MODEL_VERSION` | Specific model (e.g. `claude-opus-4-6`) |
|
|
56
|
+
| `[TOOL1] [TOOL2]` | Optional specialized analysis tools; omit everyday tools |
|
|
57
|
+
|
|
58
|
+
* Place it at the **end**, after a blank line: a git trailer in commits, the last line
|
|
59
|
+
of the body everywhere else.
|
|
60
|
+
* Skip it only for text the user dictates verbatim.
|
|
61
|
+
* Use only `Assisted-by` — no `Co-Authored-By`, no `Made with …`, no hand-written `Sent
|
|
62
|
+
using …`, no other footers.
|
|
63
|
+
|
|
64
|
+
Example:
|
|
65
|
+
|
|
66
|
+
```
|
|
67
|
+
Assisted-by: Claude:claude-opus-4-6 coccinelle sparse
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
---
|
|
71
|
+
|
|
72
|
+
## Code style
|
|
73
|
+
|
|
74
|
+
Follow existing project conventions.
|
|
75
|
+
|
|
76
|
+
* Match formatting, naming, and file structure already in use
|
|
77
|
+
* Wrap prose and Markdown matching `line-length` in `pyproject.toml`; leave code blocks,
|
|
78
|
+
tables, and URLs unwrapped
|
|
79
|
+
* Do not reformat unrelated code
|
|
80
|
+
* Prefer small, focused changes
|
|
81
|
+
* Avoid introducing new patterns without clear benefit
|
|
82
|
+
|
|
83
|
+
### Language-specific rules
|
|
84
|
+
|
|
85
|
+
* Respect `.editorconfig` when present
|
|
86
|
+
* Do not disable lint rules without justification
|
|
87
|
+
* Prefer explicit, readable code over clever abstractions
|
|
88
|
+
* Ensure all changes pass `ruff check .`, `ruff format --check .`, `pyright`, and
|
|
89
|
+
`pytest`
|
|
90
|
+
|
|
91
|
+
---
|
|
92
|
+
|
|
93
|
+
## `uv` Workflow Rules
|
|
94
|
+
|
|
95
|
+
* Use `uv` exclusively for dependency management instead of `pip`
|
|
96
|
+
* Always prefix tool and script invocations with `uv run` so they execute inside the
|
|
97
|
+
managed environment
|
|
98
|
+
* Do not manually create, activate, or delete `.venv` directories
|
|
99
|
+
|
|
100
|
+
---
|
|
101
|
+
|
|
102
|
+
## Vendor neutrality
|
|
103
|
+
|
|
104
|
+
This server fronts **any** OpenAPI service. Nothing in `src/`, `tests/`,
|
|
105
|
+
`config.example.json` or the docs may name a specific vendor, brand, product, internal
|
|
106
|
+
hostname, or employer.
|
|
107
|
+
|
|
108
|
+
* Example configs use `example.com` and placeholder names (`acme`, `items`)
|
|
109
|
+
* A behavior that exists because one upstream happens to work a certain way is
|
|
110
|
+
documented by the **property** it relies on, never by who it was observed in
|
|
111
|
+
* Vendor-specific login belongs in a token helper, in its own repository — see
|
|
112
|
+
`docs/token-protocol.md`
|
|
113
|
+
* The `[OpenAPI]` tool-description prefix required below names the protocol this server
|
|
114
|
+
speaks, not a vendor, and is not an exception to this rule
|
|
115
|
+
|
|
116
|
+
---
|
|
117
|
+
|
|
118
|
+
## MCP Metadata
|
|
119
|
+
|
|
120
|
+
Normative, high-density metadata: enough for correct tool and parameter selection,
|
|
121
|
+
minimal to reduce token cost.
|
|
122
|
+
|
|
123
|
+
* **The tool/parameter description MUST start with [OpenAPI], followed by a Verb-Object
|
|
124
|
+
fragment**, e.g. `[OpenAPI] Execute an API operation`, `[OpenAPI] List OpenAPI
|
|
125
|
+
operations`, `[OpenAPI] Get the full OpenAPI schema`.
|
|
126
|
+
* **Use tag-based lineage (Src: <Entity>) for parameters that refer to entities the
|
|
127
|
+
server can enumerate** (e.g. platform → Src: platforms, region → Src: regions, service
|
|
128
|
+
→ Src: services, operation id → Src: operations).
|
|
129
|
+
* **Every tool MUST declare `ToolAnnotations`** — hints, not guarantees, that a host
|
|
130
|
+
turns into a consent prompt.
|
|
131
|
+
* `read_only_hint=True` claims the tool changes nothing anywhere: writing a local file
|
|
132
|
+
is a change.
|
|
133
|
+
* `destructive_hint` and `idempotent_hint` matter only when `read_only_hint=False` — set
|
|
134
|
+
both there, omit both otherwise. Both are positive claims: `destructive_hint=False`
|
|
135
|
+
promises additive-only writes, `idempotent_hint=True` promises a repeat call with the
|
|
136
|
+
same arguments has no further effect.
|
|
137
|
+
* `open_world_hint` tracks the domain of interaction, not the I/O — `False` only when
|
|
138
|
+
that domain is fixed at build time (bundled data, local config).
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
@AGENTS.md
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Jon X
|
|
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,284 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: mcp-openapix
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: A Model Context Protocol (MCP) server that fronts any OpenAPI service: discover operations from its spec and call them, with bearer tokens supplied by pluggable helper commands.
|
|
5
|
+
Project-URL: Homepage, https://github.com/alyiox/mcp-openapix
|
|
6
|
+
Project-URL: Repository, https://github.com/alyiox/mcp-openapix.git
|
|
7
|
+
Project-URL: Issues, https://github.com/alyiox/mcp-openapix/issues
|
|
8
|
+
Author-email: Joh X <alyiox@hotmail.com>
|
|
9
|
+
Maintainer-email: Joh X <alyiox@hotmail.com>
|
|
10
|
+
License-Expression: MIT
|
|
11
|
+
License-File: LICENSE
|
|
12
|
+
Keywords: agent,api,llm,mcp,model-context-protocol,openapi,rest,swagger
|
|
13
|
+
Classifier: Development Status :: 3 - Alpha
|
|
14
|
+
Classifier: Environment :: Console
|
|
15
|
+
Classifier: Intended Audience :: Developers
|
|
16
|
+
Classifier: Intended Audience :: System Administrators
|
|
17
|
+
Classifier: Operating System :: OS Independent
|
|
18
|
+
Classifier: Programming Language :: Python :: 3
|
|
19
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
21
|
+
Classifier: Topic :: Internet :: WWW/HTTP
|
|
22
|
+
Classifier: Topic :: Software Development :: Libraries
|
|
23
|
+
Requires-Python: >=3.13
|
|
24
|
+
Requires-Dist: filelock>=3.30
|
|
25
|
+
Requires-Dist: httpx>=0.27
|
|
26
|
+
Requires-Dist: mcp[cli]>=2.0.0
|
|
27
|
+
Requires-Dist: pydantic>=2.0
|
|
28
|
+
Provides-Extra: dev
|
|
29
|
+
Requires-Dist: anyio>=4.0; extra == 'dev'
|
|
30
|
+
Requires-Dist: pre-commit>=4.0; extra == 'dev'
|
|
31
|
+
Requires-Dist: pyright>=1.1; extra == 'dev'
|
|
32
|
+
Requires-Dist: pytest-asyncio>=0.24; extra == 'dev'
|
|
33
|
+
Requires-Dist: pytest-cov>=6.0; extra == 'dev'
|
|
34
|
+
Requires-Dist: pytest>=8.0.0; extra == 'dev'
|
|
35
|
+
Requires-Dist: respx>=0.21; extra == 'dev'
|
|
36
|
+
Requires-Dist: ruff>=0.15; extra == 'dev'
|
|
37
|
+
Description-Content-Type: text/markdown
|
|
38
|
+
|
|
39
|
+
# mcp-openapix
|
|
40
|
+
|
|
41
|
+
[](https://github.com/alyiox/mcp-openapix/actions/workflows/ci.yml)
|
|
42
|
+
[](https://pypi.org/project/mcp-openapix/)
|
|
43
|
+
[](https://www.python.org/downloads/)
|
|
45
|
+
[](LICENSE)
|
|
46
|
+
|
|
47
|
+
<!-- mcp-name: io.github.alyiox/mcp-openapix -->
|
|
48
|
+
|
|
49
|
+
[MCP](https://modelcontextprotocol.io) server that fronts **any** OpenAPI service behind
|
|
50
|
+
four generic tools.
|
|
51
|
+
|
|
52
|
+
An agent finds operations in each deployment's OpenAPI document and calls them; the
|
|
53
|
+
server resolves the URL, obtains a bearer token, and builds the request. Discovery is
|
|
54
|
+
`list_platforms`, `list_endpoints` and `describe_endpoint`; execution is the generic
|
|
55
|
+
proxy `call_endpoint`.
|
|
56
|
+
|
|
57
|
+
```
|
|
58
|
+
example / us / items / prod
|
|
59
|
+
│ │ │ └── env ......... which deployment URL a call reaches
|
|
60
|
+
│ │ └───────── service ..... one backend, one OpenAPI spec
|
|
61
|
+
│ └─────────────── region ...... a geographic deployment
|
|
62
|
+
└───────────────────── platform .... the product or API family
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## Requirements
|
|
66
|
+
|
|
67
|
+
- Python 3.13+ and [`uv`](https://docs.astral.sh/uv/)
|
|
68
|
+
- A `config.json` describing the deployments you hold credentials for
|
|
69
|
+
|
|
70
|
+
## Quick start
|
|
71
|
+
|
|
72
|
+
Set up your config (see [Configuration](#configuration)), then run the server:
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
# Run directly with uvx (no clone needed)
|
|
76
|
+
npx -y @modelcontextprotocol/inspector@latest uvx mcp-openapix
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
# Or run from source
|
|
81
|
+
npx -y @modelcontextprotocol/inspector@latest uv run mcp-openapix
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
## Configuration
|
|
85
|
+
|
|
86
|
+
`config.json` MUST live at `~/.config/mcp-openapix/config.json`
|
|
87
|
+
(`%USERPROFILE%\.config\…` on Windows). `config.example.json` is a full template.
|
|
88
|
+
|
|
89
|
+
```json
|
|
90
|
+
{
|
|
91
|
+
"headers": { "accept": "application/json" },
|
|
92
|
+
"defaults": { "platform": "example", "region": "us", "service": "items", "env": "prod" },
|
|
93
|
+
"platforms": {
|
|
94
|
+
"example": {
|
|
95
|
+
"regions": {
|
|
96
|
+
"us": {
|
|
97
|
+
"services": {
|
|
98
|
+
"token_helper": "us",
|
|
99
|
+
"items": {
|
|
100
|
+
"desc": "Catalogue and inventory API",
|
|
101
|
+
"spec_path": "/swagger/v1/swagger.json",
|
|
102
|
+
"canonical_env": "prod",
|
|
103
|
+
"envs": {
|
|
104
|
+
"prod": { "url": "https://api.example.com/items" },
|
|
105
|
+
"dev": { "url": "https://api-dev.example.com/items" }
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
},
|
|
113
|
+
"token_helpers": {
|
|
114
|
+
"us": {
|
|
115
|
+
"command": "token-helper",
|
|
116
|
+
"args": ["issue"]
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
### `platforms`
|
|
123
|
+
|
|
124
|
+
A hierarchy of `platform → region → services → service → env`. Each service declares:
|
|
125
|
+
|
|
126
|
+
| Field | Notes |
|
|
127
|
+
|---|---|
|
|
128
|
+
| `spec_path` | Required. The OpenAPI JSON endpoint relative to the service URL |
|
|
129
|
+
| `canonical_env` | Required when more than one env is configured — the env whose URL the spec is fetched from |
|
|
130
|
+
| `envs` | Required. One entry per deployment environment, each carrying a full base `url` |
|
|
131
|
+
| `desc` | Optional. A short description surfaced by `list_platforms` |
|
|
132
|
+
| `token_helper` | Optional. The token helper this level binds to |
|
|
133
|
+
|
|
134
|
+
The `services` object may also contain a `token_helper` default applying to all services
|
|
135
|
+
in that region. A service or environment can override it.
|
|
136
|
+
|
|
137
|
+
### `token_helpers`
|
|
138
|
+
|
|
139
|
+
Named token helpers, in the same shape as an MCP server entry:
|
|
140
|
+
|
|
141
|
+
| Field | Required | Default | Notes |
|
|
142
|
+
|---|---|---|---|
|
|
143
|
+
| `command` | yes | — | Resolved on `PATH`; never run through a shell |
|
|
144
|
+
| `args` | no | `[]` | Passed verbatim |
|
|
145
|
+
| `timeout` | no | `60` | Seconds before the helper's process group is killed; at most `300` |
|
|
146
|
+
|
|
147
|
+
The config names a command and nothing else, so `config.json` holds **no secrets**.
|
|
148
|
+
The complete helper invocation and output contract is documented in
|
|
149
|
+
[`docs/token-protocol.md`](docs/token-protocol.md).
|
|
150
|
+
|
|
151
|
+
Which helper a call uses is resolved most-specific-first:
|
|
152
|
+
|
|
153
|
+
```
|
|
154
|
+
env.token_helper → service.token_helper → services.token_helper
|
|
155
|
+
→ region.token_helper → platform.token_helper → defaults.token_helper
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
If no level declares a helper, the deployment is unauthenticated. Omit
|
|
159
|
+
`token_helper` for public deployments.
|
|
160
|
+
|
|
161
|
+
### `headers`
|
|
162
|
+
|
|
163
|
+
Constant headers added to every API call — for APIs that require a tenant, product or
|
|
164
|
+
locale header:
|
|
165
|
+
|
|
166
|
+
```json
|
|
167
|
+
"headers": { "accept": "application/json", "x-product": "example" }
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
### `defaults`
|
|
171
|
+
|
|
172
|
+
Makes every tool argument optional: a call falls back to `defaults.platform`, `.region`,
|
|
173
|
+
`.service`, `.env`, `.username` and `.token_helper` when they are omitted.
|
|
174
|
+
|
|
175
|
+
### Top-level options
|
|
176
|
+
|
|
177
|
+
| Field | Default | Notes |
|
|
178
|
+
|---|---|---|
|
|
179
|
+
| `truncate_threshold` | `1024` | Response bytes returned inline before truncating to a preview |
|
|
180
|
+
| `response_cache_ttl` | `3600` | Seconds a truncated body stays readable at its resource URI |
|
|
181
|
+
| `spec_refresh` | `{"auto": true, "interval": 7}` | Background spec refresh; `interval` is days and MAY be fractional |
|
|
182
|
+
|
|
183
|
+
## Tools
|
|
184
|
+
|
|
185
|
+
| Tool | Purpose |
|
|
186
|
+
|---|---|
|
|
187
|
+
| `list_platforms` | Every platform with its regions, services, and envs |
|
|
188
|
+
| `list_endpoints` | A service's operations, filtered by `query`, `tag` or `method` |
|
|
189
|
+
| `describe_endpoint` | One operation plus the transitive closure of the schemas it references |
|
|
190
|
+
| `call_endpoint` | Execute an operation, or a raw `method` + `path` absent from the spec |
|
|
191
|
+
|
|
192
|
+
### Operation ids
|
|
193
|
+
|
|
194
|
+
Many OpenAPI documents omit `operationId`, so the server synthesizes one as `"<METHOD>
|
|
195
|
+
<path>"`:
|
|
196
|
+
|
|
197
|
+
```
|
|
198
|
+
POST /api/items
|
|
199
|
+
└─┬─┘ └───┬───┘
|
|
200
|
+
method path as the spec declares it
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
Where a spec does declare an `operationId`, that value wins.
|
|
204
|
+
|
|
205
|
+
## Specs
|
|
206
|
+
|
|
207
|
+
Specs are **not** bundled. Each deployment's document is fetched on demand — an
|
|
208
|
+
unauthenticated `GET` — and cached under
|
|
209
|
+
`~/.cache/mcp-openapix/{platform}/{region}/{service}.json`.
|
|
210
|
+
|
|
211
|
+
A document MUST declare at least one operation before it is installed, so a deployment
|
|
212
|
+
answering `200` with an error body cannot replace a working snapshot with one that
|
|
213
|
+
serves nothing.
|
|
214
|
+
|
|
215
|
+
Cached specs refresh in the background: once at startup, then every
|
|
216
|
+
`spec_refresh.interval` days. Set `auto` to `false` to stop it; the manual lever still
|
|
217
|
+
works:
|
|
218
|
+
|
|
219
|
+
```bash
|
|
220
|
+
uvx mcp-openapix --refresh
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
## MCP resources
|
|
224
|
+
|
|
225
|
+
| Resource URI | Description |
|
|
226
|
+
|---|---|
|
|
227
|
+
| `openapi://responses/{request_id}` | Full body of a truncated `call_endpoint` response |
|
|
228
|
+
| `openapi://curl/{request_id}` | Equivalent curl command for a `call_endpoint` request |
|
|
229
|
+
|
|
230
|
+
Both expire `response_cache_ttl` seconds after the call. The curl command may embed
|
|
231
|
+
a short-lived token.
|
|
232
|
+
|
|
233
|
+
## Tokens at rest
|
|
234
|
+
|
|
235
|
+
Tokens are cached in memory and, when expiry metadata is available, under
|
|
236
|
+
`~/.cache/mcp-openapix/tokens/` (mode `0600`) keyed by the token-helper declaration
|
|
237
|
+
and username. This lets client sessions share a login without spawning a helper each.
|
|
238
|
+
A `401` retires the cached token so the next call obtains a fresh one. To clear them all:
|
|
239
|
+
|
|
240
|
+
```bash
|
|
241
|
+
uvx mcp-openapix --logout
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
## MCP host examples
|
|
245
|
+
|
|
246
|
+
<details> <summary><b>Cursor / Claude Code</b></summary>
|
|
247
|
+
|
|
248
|
+
```json
|
|
249
|
+
{
|
|
250
|
+
"mcpServers": {
|
|
251
|
+
"openapi": { "command": "uvx", "args": ["mcp-openapix"] }
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
</details>
|
|
257
|
+
|
|
258
|
+
<details> <summary><b>Codex</b></summary>
|
|
259
|
+
|
|
260
|
+
```toml
|
|
261
|
+
[mcp_servers.openapi]
|
|
262
|
+
command = "uvx"
|
|
263
|
+
args = ["mcp-openapix"]
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
</details>
|
|
267
|
+
|
|
268
|
+
## Development
|
|
269
|
+
|
|
270
|
+
```bash
|
|
271
|
+
uv sync --extra dev
|
|
272
|
+
uv run ruff check .
|
|
273
|
+
uv run ruff format --check .
|
|
274
|
+
uv run pyright
|
|
275
|
+
uv run pytest
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
All four MUST pass; see `AGENTS.md`. Tests use
|
|
279
|
+
[`respx`](https://github.com/lundberg/respx) to mock HTTP and real subprocesses for
|
|
280
|
+
token helpers, so no live API access is required.
|
|
281
|
+
|
|
282
|
+
## License
|
|
283
|
+
|
|
284
|
+
[MIT](LICENSE).
|