cloud-finops-mcp 1.21.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.
- cloud_finops_mcp-1.21.0/.gitignore +53 -0
- cloud_finops_mcp-1.21.0/PKG-INFO +218 -0
- cloud_finops_mcp-1.21.0/README.md +187 -0
- cloud_finops_mcp-1.21.0/pyproject.toml +86 -0
- cloud_finops_mcp-1.21.0/scripts/sync_references.py +92 -0
- cloud_finops_mcp-1.21.0/src/cloud_finops_mcp/__init__.py +14 -0
- cloud_finops_mcp-1.21.0/src/cloud_finops_mcp/__main__.py +16 -0
- cloud_finops_mcp-1.21.0/src/cloud_finops_mcp/data/.gitignore +3 -0
- cloud_finops_mcp-1.21.0/src/cloud_finops_mcp/data/finops-ai-dev-tools.md +445 -0
- cloud_finops_mcp-1.21.0/src/cloud_finops_mcp/data/finops-ai-self-hosted-vs-managed.md +300 -0
- cloud_finops_mcp-1.21.0/src/cloud_finops_mcp/data/finops-ai-value-management.md +324 -0
- cloud_finops_mcp-1.21.0/src/cloud_finops_mcp/data/finops-allocation-showback.md +391 -0
- cloud_finops_mcp-1.21.0/src/cloud_finops_mcp/data/finops-anomaly-management.md +325 -0
- cloud_finops_mcp-1.21.0/src/cloud_finops_mcp/data/finops-anthropic.md +288 -0
- cloud_finops_mcp-1.21.0/src/cloud_finops_mcp/data/finops-aws.md +2657 -0
- cloud_finops_mcp-1.21.0/src/cloud_finops_mcp/data/finops-azure-openai.md +418 -0
- cloud_finops_mcp-1.21.0/src/cloud_finops_mcp/data/finops-azure.md +2997 -0
- cloud_finops_mcp-1.21.0/src/cloud_finops_mcp/data/finops-bedrock.md +361 -0
- cloud_finops_mcp-1.21.0/src/cloud_finops_mcp/data/finops-chargeback.md +394 -0
- cloud_finops_mcp-1.21.0/src/cloud_finops_mcp/data/finops-databricks.md +454 -0
- cloud_finops_mcp-1.21.0/src/cloud_finops_mcp/data/finops-fabric.md +320 -0
- cloud_finops_mcp-1.21.0/src/cloud_finops_mcp/data/finops-for-ai.md +442 -0
- cloud_finops_mcp-1.21.0/src/cloud_finops_mcp/data/finops-framework.md +398 -0
- cloud_finops_mcp-1.21.0/src/cloud_finops_mcp/data/finops-gcp.md +550 -0
- cloud_finops_mcp-1.21.0/src/cloud_finops_mcp/data/finops-genai-capacity.md +237 -0
- cloud_finops_mcp-1.21.0/src/cloud_finops_mcp/data/finops-itam.md +339 -0
- cloud_finops_mcp-1.21.0/src/cloud_finops_mcp/data/finops-kubernetes.md +438 -0
- cloud_finops_mcp-1.21.0/src/cloud_finops_mcp/data/finops-oci.md +180 -0
- cloud_finops_mcp-1.21.0/src/cloud_finops_mcp/data/finops-onboarding-workloads.md +418 -0
- cloud_finops_mcp-1.21.0/src/cloud_finops_mcp/data/finops-sam.md +301 -0
- cloud_finops_mcp-1.21.0/src/cloud_finops_mcp/data/finops-snowflake.md +316 -0
- cloud_finops_mcp-1.21.0/src/cloud_finops_mcp/data/finops-tagging.md +259 -0
- cloud_finops_mcp-1.21.0/src/cloud_finops_mcp/data/finops-vertexai.md +295 -0
- cloud_finops_mcp-1.21.0/src/cloud_finops_mcp/data/finops-waste-detection-playbooks.md +655 -0
- cloud_finops_mcp-1.21.0/src/cloud_finops_mcp/data/greenops-cloud-carbon.md +650 -0
- cloud_finops_mcp-1.21.0/src/cloud_finops_mcp/data/optimnow-methodology.md +163 -0
- cloud_finops_mcp-1.21.0/src/cloud_finops_mcp/data/playbooks/aws-cross-az-egress.md +92 -0
- cloud_finops_mcp-1.21.0/src/cloud_finops_mcp/data/playbooks/aws-idle-load-balancer.md +86 -0
- cloud_finops_mcp-1.21.0/src/cloud_finops_mcp/data/playbooks/aws-orphaned-ebs-volumes.md +92 -0
- cloud_finops_mcp-1.21.0/src/cloud_finops_mcp/data/playbooks/aws-oversized-rds.md +92 -0
- cloud_finops_mcp-1.21.0/src/cloud_finops_mcp/data/playbooks/aws-snapshot-sprawl.md +86 -0
- cloud_finops_mcp-1.21.0/src/cloud_finops_mcp/data/playbooks/aws-zombie-nat-gateway.md +91 -0
- cloud_finops_mcp-1.21.0/src/cloud_finops_mcp/data/playbooks/azure-app-service-overprovisioned.md +107 -0
- cloud_finops_mcp-1.21.0/src/cloud_finops_mcp/data/playbooks/azure-idle-sql-database.md +84 -0
- cloud_finops_mcp-1.21.0/src/cloud_finops_mcp/data/playbooks/azure-log-analytics-sprawl.md +87 -0
- cloud_finops_mcp-1.21.0/src/cloud_finops_mcp/data/playbooks/azure-orphan-disks.md +90 -0
- cloud_finops_mcp-1.21.0/src/cloud_finops_mcp/data/playbooks/cross-cloud-schedule-blindness.md +115 -0
- cloud_finops_mcp-1.21.0/src/cloud_finops_mcp/data/playbooks/cross-cloud-untagged-spend-drift.md +132 -0
- cloud_finops_mcp-1.21.0/src/cloud_finops_mcp/data/playbooks/gcp-cloud-functions-cold-starts.md +108 -0
- cloud_finops_mcp-1.21.0/src/cloud_finops_mcp/data/playbooks/gcp-idle-gke-autopilot.md +90 -0
- cloud_finops_mcp-1.21.0/src/cloud_finops_mcp/data/playbooks/gcp-orphan-persistent-disks.md +86 -0
- cloud_finops_mcp-1.21.0/src/cloud_finops_mcp/metadata.py +246 -0
- cloud_finops_mcp-1.21.0/src/cloud_finops_mcp/server.py +177 -0
- cloud_finops_mcp-1.21.0/src/cloud_finops_mcp/tools.py +238 -0
- cloud_finops_mcp-1.21.0/tests/__init__.py +0 -0
- cloud_finops_mcp-1.21.0/tests/conftest.py +42 -0
- cloud_finops_mcp-1.21.0/tests/test_e2e.py +133 -0
- cloud_finops_mcp-1.21.0/tests/test_metadata.py +78 -0
- cloud_finops_mcp-1.21.0/tests/test_tools.py +191 -0
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# Internal
|
|
2
|
+
INTERNAL_NOTES.md
|
|
3
|
+
|
|
4
|
+
# Environment
|
|
5
|
+
.env
|
|
6
|
+
.env.*
|
|
7
|
+
|
|
8
|
+
# OS files
|
|
9
|
+
.DS_Store
|
|
10
|
+
Thumbs.db
|
|
11
|
+
NUL
|
|
12
|
+
|
|
13
|
+
# Claude Code local settings (any depth - root, cloud-finops/, etc.)
|
|
14
|
+
.claude/settings.local.json
|
|
15
|
+
**/.claude/settings.local.json
|
|
16
|
+
|
|
17
|
+
# Claude Code local hooks config
|
|
18
|
+
claude-hooks-settings.json
|
|
19
|
+
|
|
20
|
+
# Claude Code worktrees (embedded git repos)
|
|
21
|
+
.claude/worktrees/
|
|
22
|
+
|
|
23
|
+
# Local-only nested .claude/ directories (e.g. cloud-finops/.claude/)
|
|
24
|
+
cloud-finops/.claude/
|
|
25
|
+
|
|
26
|
+
# Local working notes / plan drafts
|
|
27
|
+
plan-*.md
|
|
28
|
+
plan-remediation-*.md
|
|
29
|
+
|
|
30
|
+
# Pipeline (private until public release)
|
|
31
|
+
pipeline/
|
|
32
|
+
|
|
33
|
+
# Reference file backups
|
|
34
|
+
cloud-finops/references/.backups/
|
|
35
|
+
|
|
36
|
+
# Distribution archive
|
|
37
|
+
cloud-finops.zip
|
|
38
|
+
|
|
39
|
+
# Python build artefacts (mcp_server/)
|
|
40
|
+
__pycache__/
|
|
41
|
+
*.py[cod]
|
|
42
|
+
*.egg-info/
|
|
43
|
+
.pytest_cache/
|
|
44
|
+
.ruff_cache/
|
|
45
|
+
mcp_server/build/
|
|
46
|
+
mcp_server/dist/
|
|
47
|
+
mcp_server/.venv/
|
|
48
|
+
|
|
49
|
+
# LinkedIn post drafts
|
|
50
|
+
linkedin-post-*.md
|
|
51
|
+
|
|
52
|
+
# Review adjudication
|
|
53
|
+
review-adjudication.md
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: cloud-finops-mcp
|
|
3
|
+
Version: 1.21.0
|
|
4
|
+
Summary: MCP server exposing the OptimNow Cloud FinOps skill (28 references) as queryable tools.
|
|
5
|
+
Project-URL: Homepage, https://github.com/OptimNow/cloud-finops-skills
|
|
6
|
+
Project-URL: Repository, https://github.com/OptimNow/cloud-finops-skills
|
|
7
|
+
Project-URL: Issues, https://github.com/OptimNow/cloud-finops-skills/issues
|
|
8
|
+
Project-URL: Documentation, https://github.com/OptimNow/cloud-finops-skills/tree/main/mcp_server
|
|
9
|
+
Author-email: OptimNow <contact@optimnow.io>
|
|
10
|
+
Maintainer-email: Jean Latière <j.latiere@gmail.com>
|
|
11
|
+
License: CC-BY-SA-4.0
|
|
12
|
+
Keywords: aws,azure,claude,cloud,cost,finops,gcp,mcp
|
|
13
|
+
Classifier: Development Status :: 4 - Beta
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: Intended Audience :: Information Technology
|
|
16
|
+
Classifier: License :: OSI Approved
|
|
17
|
+
Classifier: Operating System :: OS Independent
|
|
18
|
+
Classifier: Programming Language :: Python :: 3
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
22
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
23
|
+
Requires-Python: >=3.10
|
|
24
|
+
Requires-Dist: mcp>=1.0.0
|
|
25
|
+
Requires-Dist: pyyaml>=6.0
|
|
26
|
+
Provides-Extra: dev
|
|
27
|
+
Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
|
|
28
|
+
Requires-Dist: pytest>=7.4; extra == 'dev'
|
|
29
|
+
Requires-Dist: ruff>=0.5; extra == 'dev'
|
|
30
|
+
Description-Content-Type: text/markdown
|
|
31
|
+
|
|
32
|
+
# cloud-finops-mcp
|
|
33
|
+
|
|
34
|
+
MCP server exposing the [OptimNow Cloud FinOps skill](https://github.com/OptimNow/cloud-finops-skills)
|
|
35
|
+
(28 reference files + 15 named-pattern playbooks) as queryable tools for any
|
|
36
|
+
MCP-aware client (Claude Code, Cursor, Codex CLI, Windsurf, Aider, Cline, etc.).
|
|
37
|
+
|
|
38
|
+
The skill itself ships in canonical Claude Agent-Skills format and is also installable
|
|
39
|
+
via the cross-tool installer (`./install.sh`) for direct context injection. This MCP
|
|
40
|
+
server is the **enrichment path**: instead of loading the full skill into the
|
|
41
|
+
prompt, the agent calls tools to discover, filter, and fetch only what it needs.
|
|
42
|
+
|
|
43
|
+
## What the server exposes
|
|
44
|
+
|
|
45
|
+
Six tools, all read-only, split across two surfaces.
|
|
46
|
+
|
|
47
|
+
**References** — long-form provider and discipline files (~300-500 lines each):
|
|
48
|
+
|
|
49
|
+
| Tool | Purpose |
|
|
50
|
+
|---|---|
|
|
51
|
+
| `list_references()` | List all 28 references with their FCP metadata. |
|
|
52
|
+
| `get_reference(name)` | Fetch the full markdown body of one reference. |
|
|
53
|
+
| `find_references(domain?, capability?, phase?, persona?, maturity?)` | Faceted query over the FinOps Capability/Phase frontmatter. |
|
|
54
|
+
|
|
55
|
+
The reference faceted query supports any combination of:
|
|
56
|
+
|
|
57
|
+
- `domain` - FinOps Framework domain (e.g. `Optimize Usage & Cost`, `Quantify Business Value`)
|
|
58
|
+
- `capability` - FinOps capability (matches both primary and secondary)
|
|
59
|
+
- `phase` - `Inform`, `Optimize`, `Operate`
|
|
60
|
+
- `persona` - matches both primary and collaborating personas
|
|
61
|
+
- `maturity` - `Crawl`, `Walk`, `Run`
|
|
62
|
+
|
|
63
|
+
**Playbooks** — small named-pattern runbooks (~80-130 lines each):
|
|
64
|
+
|
|
65
|
+
| Tool | Purpose |
|
|
66
|
+
|---|---|
|
|
67
|
+
| `list_playbooks()` | List all 15 named-pattern playbooks with their metadata. |
|
|
68
|
+
| `get_playbook(name)` | Fetch the full markdown body of one playbook. |
|
|
69
|
+
| `find_playbooks(scope?, service?, waste_category?, confidence?)` | Faceted query over the playbook frontmatter. |
|
|
70
|
+
|
|
71
|
+
The playbook faceted query supports:
|
|
72
|
+
|
|
73
|
+
- `scope` - `aws`, `azure`, `gcp`, or `cross-cloud`
|
|
74
|
+
- `service` - provider service (e.g. `AWS NAT Gateway`); exact-match
|
|
75
|
+
- `waste_category` - `orphaned`, `idle`, `overprovisioned`, `commitment-mismatch`,
|
|
76
|
+
`schedule-blindness`, `modernization`, `ai-ml-inefficiency`, `egress`
|
|
77
|
+
- `confidence` - `obvious`, `likely`, `possible` (OptimNow three-tier model)
|
|
78
|
+
|
|
79
|
+
All filters across both surfaces AND together. String matches are case-insensitive
|
|
80
|
+
and exact (no substring matching).
|
|
81
|
+
|
|
82
|
+
**When to use which surface:**
|
|
83
|
+
|
|
84
|
+
- A **playbook** answers *"how do I detect/fix this specific pattern?"* (zombie NAT,
|
|
85
|
+
snapshot sprawl, idle ELB). It includes problem statement, symptoms, a detection
|
|
86
|
+
query (CUR / KQL / BigQuery SQL / CLI), fix steps, and the anti-pattern.
|
|
87
|
+
- A **reference** answers anything broader: billing mechanics, commitment strategy,
|
|
88
|
+
allocation methodology, persona-specific framings, or cross-pattern reasoning.
|
|
89
|
+
|
|
90
|
+
## Install
|
|
91
|
+
|
|
92
|
+
```bash
|
|
93
|
+
pip install cloud-finops-mcp
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
Or run without installing via [`uv`](https://docs.astral.sh/uv/):
|
|
97
|
+
|
|
98
|
+
```bash
|
|
99
|
+
uvx cloud-finops-mcp
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
## Configure your MCP client
|
|
103
|
+
|
|
104
|
+
After install, point your client at the `cloud-finops-mcp` console script.
|
|
105
|
+
|
|
106
|
+
### Claude Code
|
|
107
|
+
|
|
108
|
+
Project-level (`.mcp.json` at the repo root) or user-level (`~/.claude/mcp.json`):
|
|
109
|
+
|
|
110
|
+
```json
|
|
111
|
+
{
|
|
112
|
+
"mcpServers": {
|
|
113
|
+
"cloud-finops": {
|
|
114
|
+
"command": "cloud-finops-mcp"
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
Restart Claude Code, then run `/mcp` to confirm the server is connected.
|
|
121
|
+
|
|
122
|
+
### Cursor
|
|
123
|
+
|
|
124
|
+
`~/.cursor/mcp.json`:
|
|
125
|
+
|
|
126
|
+
```json
|
|
127
|
+
{
|
|
128
|
+
"mcpServers": {
|
|
129
|
+
"cloud-finops": {
|
|
130
|
+
"command": "cloud-finops-mcp"
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
### Codex CLI
|
|
137
|
+
|
|
138
|
+
`~/.codex/config.toml`:
|
|
139
|
+
|
|
140
|
+
```toml
|
|
141
|
+
[mcp_servers.cloud-finops]
|
|
142
|
+
command = "cloud-finops-mcp"
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
### Windsurf
|
|
146
|
+
|
|
147
|
+
`~/.windsurf/mcp.json`:
|
|
148
|
+
|
|
149
|
+
```json
|
|
150
|
+
{
|
|
151
|
+
"mcpServers": {
|
|
152
|
+
"cloud-finops": {
|
|
153
|
+
"command": "cloud-finops-mcp"
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
### Any other MCP client
|
|
160
|
+
|
|
161
|
+
The server speaks MCP over stdio. Point any compatible client at `cloud-finops-mcp`
|
|
162
|
+
(or `python -m cloud_finops_mcp`).
|
|
163
|
+
|
|
164
|
+
## Example tool calls
|
|
165
|
+
|
|
166
|
+
Agent prompt: *"Use the cloud-finops MCP to find references for the Optimize phase
|
|
167
|
+
aimed at Engineering."*
|
|
168
|
+
|
|
169
|
+
Calls `find_references(phase="Optimize", persona="Engineering")` and gets back the
|
|
170
|
+
filtered subset (AWS, Azure, GCP, Bedrock, Databricks, etc.) without loading the full
|
|
171
|
+
skill into the prompt.
|
|
172
|
+
|
|
173
|
+
Agent prompt: *"Pull the AWS reference."*
|
|
174
|
+
|
|
175
|
+
Calls `get_reference(name="finops-aws")` and gets back the full markdown body
|
|
176
|
+
(~300 lines) instead of the entire 28-file knowledge base.
|
|
177
|
+
|
|
178
|
+
Agent prompt: *"Show me the obvious-confidence AWS waste playbooks."*
|
|
179
|
+
|
|
180
|
+
Calls `find_playbooks(scope="aws", confidence="obvious")` and gets back the list
|
|
181
|
+
of high-signal AWS patterns (zombie NAT gateway, orphaned EBS volumes, etc.).
|
|
182
|
+
|
|
183
|
+
Agent prompt: *"Walk me through the zombie NAT gateway pattern."*
|
|
184
|
+
|
|
185
|
+
Calls `get_playbook(name="aws-zombie-nat-gateway")` and gets back the ~90-line
|
|
186
|
+
runbook (problem, symptoms, detection query, fix, anti-pattern, see-also).
|
|
187
|
+
|
|
188
|
+
## When to use this vs the installer
|
|
189
|
+
|
|
190
|
+
| If you... | Use |
|
|
191
|
+
|---|---|
|
|
192
|
+
| Want the skill loaded as static context for every chat | The cross-tool installer (`./install.sh`) |
|
|
193
|
+
| Have a big-codebase session with limited context budget | The MCP server (fetch on demand) |
|
|
194
|
+
| Want to filter references by FinOps domain/capability/phase/persona/maturity | The MCP server (`find_references`) |
|
|
195
|
+
| Use a client that doesn't support MCP | The cross-tool installer |
|
|
196
|
+
|
|
197
|
+
The two paths are complementary. You can install both.
|
|
198
|
+
|
|
199
|
+
## Development
|
|
200
|
+
|
|
201
|
+
```bash
|
|
202
|
+
git clone https://github.com/OptimNow/cloud-finops-skills.git
|
|
203
|
+
cd cloud-finops-skills/mcp_server
|
|
204
|
+
python scripts/sync_references.py # populate src/cloud_finops_mcp/data/
|
|
205
|
+
pip install -e ".[dev]"
|
|
206
|
+
pytest
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
## Versioning
|
|
210
|
+
|
|
211
|
+
The PyPI package version tracks the skill release tag. Tagging `v1.13` on the
|
|
212
|
+
parent repo triggers both the skill release zip and a new `cloud-finops-mcp` PyPI
|
|
213
|
+
publish so the bundled references match what the rest of the repo ships.
|
|
214
|
+
|
|
215
|
+
## License
|
|
216
|
+
|
|
217
|
+
[CC BY-SA 4.0](https://creativecommons.org/licenses/by-sa/4.0/) - same as the parent
|
|
218
|
+
skill. Credit OptimNow.
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
# cloud-finops-mcp
|
|
2
|
+
|
|
3
|
+
MCP server exposing the [OptimNow Cloud FinOps skill](https://github.com/OptimNow/cloud-finops-skills)
|
|
4
|
+
(28 reference files + 15 named-pattern playbooks) as queryable tools for any
|
|
5
|
+
MCP-aware client (Claude Code, Cursor, Codex CLI, Windsurf, Aider, Cline, etc.).
|
|
6
|
+
|
|
7
|
+
The skill itself ships in canonical Claude Agent-Skills format and is also installable
|
|
8
|
+
via the cross-tool installer (`./install.sh`) for direct context injection. This MCP
|
|
9
|
+
server is the **enrichment path**: instead of loading the full skill into the
|
|
10
|
+
prompt, the agent calls tools to discover, filter, and fetch only what it needs.
|
|
11
|
+
|
|
12
|
+
## What the server exposes
|
|
13
|
+
|
|
14
|
+
Six tools, all read-only, split across two surfaces.
|
|
15
|
+
|
|
16
|
+
**References** — long-form provider and discipline files (~300-500 lines each):
|
|
17
|
+
|
|
18
|
+
| Tool | Purpose |
|
|
19
|
+
|---|---|
|
|
20
|
+
| `list_references()` | List all 28 references with their FCP metadata. |
|
|
21
|
+
| `get_reference(name)` | Fetch the full markdown body of one reference. |
|
|
22
|
+
| `find_references(domain?, capability?, phase?, persona?, maturity?)` | Faceted query over the FinOps Capability/Phase frontmatter. |
|
|
23
|
+
|
|
24
|
+
The reference faceted query supports any combination of:
|
|
25
|
+
|
|
26
|
+
- `domain` - FinOps Framework domain (e.g. `Optimize Usage & Cost`, `Quantify Business Value`)
|
|
27
|
+
- `capability` - FinOps capability (matches both primary and secondary)
|
|
28
|
+
- `phase` - `Inform`, `Optimize`, `Operate`
|
|
29
|
+
- `persona` - matches both primary and collaborating personas
|
|
30
|
+
- `maturity` - `Crawl`, `Walk`, `Run`
|
|
31
|
+
|
|
32
|
+
**Playbooks** — small named-pattern runbooks (~80-130 lines each):
|
|
33
|
+
|
|
34
|
+
| Tool | Purpose |
|
|
35
|
+
|---|---|
|
|
36
|
+
| `list_playbooks()` | List all 15 named-pattern playbooks with their metadata. |
|
|
37
|
+
| `get_playbook(name)` | Fetch the full markdown body of one playbook. |
|
|
38
|
+
| `find_playbooks(scope?, service?, waste_category?, confidence?)` | Faceted query over the playbook frontmatter. |
|
|
39
|
+
|
|
40
|
+
The playbook faceted query supports:
|
|
41
|
+
|
|
42
|
+
- `scope` - `aws`, `azure`, `gcp`, or `cross-cloud`
|
|
43
|
+
- `service` - provider service (e.g. `AWS NAT Gateway`); exact-match
|
|
44
|
+
- `waste_category` - `orphaned`, `idle`, `overprovisioned`, `commitment-mismatch`,
|
|
45
|
+
`schedule-blindness`, `modernization`, `ai-ml-inefficiency`, `egress`
|
|
46
|
+
- `confidence` - `obvious`, `likely`, `possible` (OptimNow three-tier model)
|
|
47
|
+
|
|
48
|
+
All filters across both surfaces AND together. String matches are case-insensitive
|
|
49
|
+
and exact (no substring matching).
|
|
50
|
+
|
|
51
|
+
**When to use which surface:**
|
|
52
|
+
|
|
53
|
+
- A **playbook** answers *"how do I detect/fix this specific pattern?"* (zombie NAT,
|
|
54
|
+
snapshot sprawl, idle ELB). It includes problem statement, symptoms, a detection
|
|
55
|
+
query (CUR / KQL / BigQuery SQL / CLI), fix steps, and the anti-pattern.
|
|
56
|
+
- A **reference** answers anything broader: billing mechanics, commitment strategy,
|
|
57
|
+
allocation methodology, persona-specific framings, or cross-pattern reasoning.
|
|
58
|
+
|
|
59
|
+
## Install
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
pip install cloud-finops-mcp
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
Or run without installing via [`uv`](https://docs.astral.sh/uv/):
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
uvx cloud-finops-mcp
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
## Configure your MCP client
|
|
72
|
+
|
|
73
|
+
After install, point your client at the `cloud-finops-mcp` console script.
|
|
74
|
+
|
|
75
|
+
### Claude Code
|
|
76
|
+
|
|
77
|
+
Project-level (`.mcp.json` at the repo root) or user-level (`~/.claude/mcp.json`):
|
|
78
|
+
|
|
79
|
+
```json
|
|
80
|
+
{
|
|
81
|
+
"mcpServers": {
|
|
82
|
+
"cloud-finops": {
|
|
83
|
+
"command": "cloud-finops-mcp"
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
Restart Claude Code, then run `/mcp` to confirm the server is connected.
|
|
90
|
+
|
|
91
|
+
### Cursor
|
|
92
|
+
|
|
93
|
+
`~/.cursor/mcp.json`:
|
|
94
|
+
|
|
95
|
+
```json
|
|
96
|
+
{
|
|
97
|
+
"mcpServers": {
|
|
98
|
+
"cloud-finops": {
|
|
99
|
+
"command": "cloud-finops-mcp"
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
### Codex CLI
|
|
106
|
+
|
|
107
|
+
`~/.codex/config.toml`:
|
|
108
|
+
|
|
109
|
+
```toml
|
|
110
|
+
[mcp_servers.cloud-finops]
|
|
111
|
+
command = "cloud-finops-mcp"
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
### Windsurf
|
|
115
|
+
|
|
116
|
+
`~/.windsurf/mcp.json`:
|
|
117
|
+
|
|
118
|
+
```json
|
|
119
|
+
{
|
|
120
|
+
"mcpServers": {
|
|
121
|
+
"cloud-finops": {
|
|
122
|
+
"command": "cloud-finops-mcp"
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
### Any other MCP client
|
|
129
|
+
|
|
130
|
+
The server speaks MCP over stdio. Point any compatible client at `cloud-finops-mcp`
|
|
131
|
+
(or `python -m cloud_finops_mcp`).
|
|
132
|
+
|
|
133
|
+
## Example tool calls
|
|
134
|
+
|
|
135
|
+
Agent prompt: *"Use the cloud-finops MCP to find references for the Optimize phase
|
|
136
|
+
aimed at Engineering."*
|
|
137
|
+
|
|
138
|
+
Calls `find_references(phase="Optimize", persona="Engineering")` and gets back the
|
|
139
|
+
filtered subset (AWS, Azure, GCP, Bedrock, Databricks, etc.) without loading the full
|
|
140
|
+
skill into the prompt.
|
|
141
|
+
|
|
142
|
+
Agent prompt: *"Pull the AWS reference."*
|
|
143
|
+
|
|
144
|
+
Calls `get_reference(name="finops-aws")` and gets back the full markdown body
|
|
145
|
+
(~300 lines) instead of the entire 28-file knowledge base.
|
|
146
|
+
|
|
147
|
+
Agent prompt: *"Show me the obvious-confidence AWS waste playbooks."*
|
|
148
|
+
|
|
149
|
+
Calls `find_playbooks(scope="aws", confidence="obvious")` and gets back the list
|
|
150
|
+
of high-signal AWS patterns (zombie NAT gateway, orphaned EBS volumes, etc.).
|
|
151
|
+
|
|
152
|
+
Agent prompt: *"Walk me through the zombie NAT gateway pattern."*
|
|
153
|
+
|
|
154
|
+
Calls `get_playbook(name="aws-zombie-nat-gateway")` and gets back the ~90-line
|
|
155
|
+
runbook (problem, symptoms, detection query, fix, anti-pattern, see-also).
|
|
156
|
+
|
|
157
|
+
## When to use this vs the installer
|
|
158
|
+
|
|
159
|
+
| If you... | Use |
|
|
160
|
+
|---|---|
|
|
161
|
+
| Want the skill loaded as static context for every chat | The cross-tool installer (`./install.sh`) |
|
|
162
|
+
| Have a big-codebase session with limited context budget | The MCP server (fetch on demand) |
|
|
163
|
+
| Want to filter references by FinOps domain/capability/phase/persona/maturity | The MCP server (`find_references`) |
|
|
164
|
+
| Use a client that doesn't support MCP | The cross-tool installer |
|
|
165
|
+
|
|
166
|
+
The two paths are complementary. You can install both.
|
|
167
|
+
|
|
168
|
+
## Development
|
|
169
|
+
|
|
170
|
+
```bash
|
|
171
|
+
git clone https://github.com/OptimNow/cloud-finops-skills.git
|
|
172
|
+
cd cloud-finops-skills/mcp_server
|
|
173
|
+
python scripts/sync_references.py # populate src/cloud_finops_mcp/data/
|
|
174
|
+
pip install -e ".[dev]"
|
|
175
|
+
pytest
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
## Versioning
|
|
179
|
+
|
|
180
|
+
The PyPI package version tracks the skill release tag. Tagging `v1.13` on the
|
|
181
|
+
parent repo triggers both the skill release zip and a new `cloud-finops-mcp` PyPI
|
|
182
|
+
publish so the bundled references match what the rest of the repo ships.
|
|
183
|
+
|
|
184
|
+
## License
|
|
185
|
+
|
|
186
|
+
[CC BY-SA 4.0](https://creativecommons.org/licenses/by-sa/4.0/) - same as the parent
|
|
187
|
+
skill. Credit OptimNow.
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling>=1.18", "hatch-build-scripts>=0.0.4"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "cloud-finops-mcp"
|
|
7
|
+
version = "1.21.0"
|
|
8
|
+
description = "MCP server exposing the OptimNow Cloud FinOps skill (28 references) as queryable tools."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
license = { text = "CC-BY-SA-4.0" }
|
|
11
|
+
authors = [
|
|
12
|
+
{ name = "OptimNow", email = "contact@optimnow.io" }
|
|
13
|
+
]
|
|
14
|
+
maintainers = [
|
|
15
|
+
{ name = "Jean Latière", email = "j.latiere@gmail.com" }
|
|
16
|
+
]
|
|
17
|
+
requires-python = ">=3.10"
|
|
18
|
+
keywords = ["finops", "cloud", "cost", "mcp", "claude", "aws", "azure", "gcp"]
|
|
19
|
+
classifiers = [
|
|
20
|
+
"Development Status :: 4 - Beta",
|
|
21
|
+
"Intended Audience :: Developers",
|
|
22
|
+
"Intended Audience :: Information Technology",
|
|
23
|
+
"License :: OSI Approved",
|
|
24
|
+
"Operating System :: OS Independent",
|
|
25
|
+
"Programming Language :: Python :: 3",
|
|
26
|
+
"Programming Language :: Python :: 3.10",
|
|
27
|
+
"Programming Language :: Python :: 3.11",
|
|
28
|
+
"Programming Language :: Python :: 3.12",
|
|
29
|
+
"Topic :: Software Development :: Libraries :: Python Modules",
|
|
30
|
+
]
|
|
31
|
+
dependencies = [
|
|
32
|
+
"mcp>=1.0.0",
|
|
33
|
+
"pyyaml>=6.0",
|
|
34
|
+
]
|
|
35
|
+
|
|
36
|
+
[project.optional-dependencies]
|
|
37
|
+
dev = [
|
|
38
|
+
"pytest>=7.4",
|
|
39
|
+
"pytest-asyncio>=0.23",
|
|
40
|
+
"ruff>=0.5",
|
|
41
|
+
]
|
|
42
|
+
|
|
43
|
+
[project.urls]
|
|
44
|
+
Homepage = "https://github.com/OptimNow/cloud-finops-skills"
|
|
45
|
+
Repository = "https://github.com/OptimNow/cloud-finops-skills"
|
|
46
|
+
Issues = "https://github.com/OptimNow/cloud-finops-skills/issues"
|
|
47
|
+
Documentation = "https://github.com/OptimNow/cloud-finops-skills/tree/main/mcp_server"
|
|
48
|
+
|
|
49
|
+
[project.scripts]
|
|
50
|
+
cloud-finops-mcp = "cloud_finops_mcp.__main__:main"
|
|
51
|
+
|
|
52
|
+
[tool.hatch.build.targets.wheel]
|
|
53
|
+
packages = ["src/cloud_finops_mcp"]
|
|
54
|
+
# .md files in src/cloud_finops_mcp/data/ are gitignored but live in the
|
|
55
|
+
# package directory; hatch picks them up via the package walk, no
|
|
56
|
+
# force-include needed.
|
|
57
|
+
|
|
58
|
+
[tool.hatch.build.targets.sdist]
|
|
59
|
+
include = [
|
|
60
|
+
"src/cloud_finops_mcp",
|
|
61
|
+
"scripts",
|
|
62
|
+
"tests",
|
|
63
|
+
"README.md",
|
|
64
|
+
"pyproject.toml",
|
|
65
|
+
]
|
|
66
|
+
# Force-include the synced reference bundle so the sdist is self-contained -
|
|
67
|
+
# without this, hatch respects ../../.gitignore and drops src/.../data/*.md.
|
|
68
|
+
[tool.hatch.build.targets.sdist.force-include]
|
|
69
|
+
"src/cloud_finops_mcp/data" = "src/cloud_finops_mcp/data"
|
|
70
|
+
|
|
71
|
+
[[tool.hatch.build.hooks.build-scripts.scripts]]
|
|
72
|
+
out_dir = "src/cloud_finops_mcp/data"
|
|
73
|
+
commands = ["python scripts/sync_references.py"]
|
|
74
|
+
artifacts = []
|
|
75
|
+
|
|
76
|
+
[tool.pytest.ini_options]
|
|
77
|
+
testpaths = ["tests"]
|
|
78
|
+
asyncio_mode = "auto"
|
|
79
|
+
|
|
80
|
+
[tool.ruff]
|
|
81
|
+
line-length = 100
|
|
82
|
+
target-version = "py310"
|
|
83
|
+
|
|
84
|
+
[tool.ruff.lint]
|
|
85
|
+
select = ["E", "F", "I", "W", "UP"]
|
|
86
|
+
ignore = ["E501"]
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Copy ``cloud-finops/references/*.md`` and ``cloud-finops/playbooks/*.md`` into
|
|
3
|
+
the bundled ``data/`` folder.
|
|
4
|
+
|
|
5
|
+
Runs automatically before each wheel build (declared in ``pyproject.toml`` as a
|
|
6
|
+
``hatch-build-scripts`` hook). Also intended to be run manually after
|
|
7
|
+
``pip install -e .`` so the editable install picks up the latest reference
|
|
8
|
+
content::
|
|
9
|
+
|
|
10
|
+
python scripts/sync_references.py
|
|
11
|
+
|
|
12
|
+
The script is idempotent and clears the destination folders first to drop
|
|
13
|
+
files that have been removed upstream. The ``playbooks/README.md`` file is
|
|
14
|
+
intentionally skipped: it documents the format for human contributors and is
|
|
15
|
+
not a runbook the agent should retrieve.
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
from __future__ import annotations
|
|
19
|
+
|
|
20
|
+
import shutil
|
|
21
|
+
import sys
|
|
22
|
+
from pathlib import Path
|
|
23
|
+
|
|
24
|
+
REPO_ROOT = Path(__file__).resolve().parents[2]
|
|
25
|
+
REFERENCES_SRC = REPO_ROOT / "cloud-finops" / "references"
|
|
26
|
+
PLAYBOOKS_SRC = REPO_ROOT / "cloud-finops" / "playbooks"
|
|
27
|
+
|
|
28
|
+
DATA_ROOT = Path(__file__).resolve().parents[1] / "src" / "cloud_finops_mcp" / "data"
|
|
29
|
+
REFERENCES_DEST = DATA_ROOT
|
|
30
|
+
PLAYBOOKS_DEST = DATA_ROOT / "playbooks"
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def _sync(label: str, src_dir: Path, dest_dir: Path, *, skip: set[str]) -> int:
|
|
34
|
+
"""Mirror ``*.md`` from ``src_dir`` into ``dest_dir``.
|
|
35
|
+
|
|
36
|
+
Returns the number of files copied. Falls back gracefully when ``src_dir`` is
|
|
37
|
+
missing (sdist-build case) but ``dest_dir`` is already populated.
|
|
38
|
+
"""
|
|
39
|
+
dest_dir.mkdir(parents=True, exist_ok=True)
|
|
40
|
+
existing = [p for p in dest_dir.glob("*.md")]
|
|
41
|
+
|
|
42
|
+
if not src_dir.is_dir():
|
|
43
|
+
if existing:
|
|
44
|
+
print(
|
|
45
|
+
f"[sync_references] {label}: source missing; using "
|
|
46
|
+
f"{len(existing)} pre-bundled file(s) at {dest_dir}"
|
|
47
|
+
)
|
|
48
|
+
return len(existing)
|
|
49
|
+
print(
|
|
50
|
+
f"[sync_references] {label}: source directory not found and no "
|
|
51
|
+
f"pre-bundled files present: {src_dir}",
|
|
52
|
+
file=sys.stderr,
|
|
53
|
+
)
|
|
54
|
+
return -1
|
|
55
|
+
|
|
56
|
+
# Wipe stale .md files so deletions upstream propagate to the bundle.
|
|
57
|
+
for stale in existing:
|
|
58
|
+
stale.unlink()
|
|
59
|
+
|
|
60
|
+
copied = 0
|
|
61
|
+
for src in sorted(src_dir.glob("*.md")):
|
|
62
|
+
if src.name in skip:
|
|
63
|
+
continue
|
|
64
|
+
shutil.copy2(src, dest_dir / src.name)
|
|
65
|
+
copied += 1
|
|
66
|
+
|
|
67
|
+
print(f"[sync_references] {label}: copied {copied} file(s) to {dest_dir}")
|
|
68
|
+
return copied
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def main() -> int:
|
|
72
|
+
refs = _sync("references", REFERENCES_SRC, REFERENCES_DEST, skip=set())
|
|
73
|
+
playbooks = _sync(
|
|
74
|
+
"playbooks", PLAYBOOKS_SRC, PLAYBOOKS_DEST, skip={"README.md"}
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
if refs < 0:
|
|
78
|
+
return 1
|
|
79
|
+
if playbooks < 0:
|
|
80
|
+
# Playbooks are optional - older skill versions may not have shipped
|
|
81
|
+
# them. Warn but do not fail the build.
|
|
82
|
+
print(
|
|
83
|
+
"[sync_references] WARNING: playbooks unavailable; the MCP "
|
|
84
|
+
"server will still expose references but list_playbooks() will "
|
|
85
|
+
"be empty.",
|
|
86
|
+
file=sys.stderr,
|
|
87
|
+
)
|
|
88
|
+
return 0
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
if __name__ == "__main__":
|
|
92
|
+
raise SystemExit(main())
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"""Cloud FinOps MCP server.
|
|
2
|
+
|
|
3
|
+
Exposes the OptimNow Cloud FinOps skill (28 references) as queryable tools
|
|
4
|
+
via the Model Context Protocol.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from importlib.metadata import PackageNotFoundError, version
|
|
8
|
+
|
|
9
|
+
try:
|
|
10
|
+
__version__ = version("cloud-finops-mcp")
|
|
11
|
+
except PackageNotFoundError: # pragma: no cover - editable / not installed
|
|
12
|
+
__version__ = "0.0.0+dev"
|
|
13
|
+
|
|
14
|
+
__all__ = ["__version__"]
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"""Entry point: ``python -m cloud_finops_mcp`` and ``cloud-finops-mcp`` console script."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import asyncio
|
|
6
|
+
|
|
7
|
+
from .server import run
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def main() -> None:
|
|
11
|
+
"""Synchronous wrapper used by the console-script entry point."""
|
|
12
|
+
asyncio.run(run())
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
if __name__ == "__main__":
|
|
16
|
+
main()
|