stacklet-mcp 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.
- stacklet_mcp-0.1.0/.github/CODEOWNERS +1 -0
- stacklet_mcp-0.1.0/.github/composites/install/action.yml +31 -0
- stacklet_mcp-0.1.0/.github/dependabot.yaml +6 -0
- stacklet_mcp-0.1.0/.github/workflows/ci.yml +35 -0
- stacklet_mcp-0.1.0/.github/workflows/release.yml +26 -0
- stacklet_mcp-0.1.0/.gitignore +21 -0
- stacklet_mcp-0.1.0/.pre-commit-config.yaml +41 -0
- stacklet_mcp-0.1.0/.tool-versions +3 -0
- stacklet_mcp-0.1.0/CHANGELOG.md +6 -0
- stacklet_mcp-0.1.0/CLAUDE.md +207 -0
- stacklet_mcp-0.1.0/PKG-INFO +13 -0
- stacklet_mcp-0.1.0/README.md +115 -0
- stacklet_mcp-0.1.0/justfile +42 -0
- stacklet_mcp-0.1.0/license-header.tpl +1 -0
- stacklet_mcp-0.1.0/pyproject.toml +74 -0
- stacklet_mcp-0.1.0/stacklet/mcp/__init__.py +8 -0
- stacklet_mcp-0.1.0/stacklet/mcp/__main__.py +11 -0
- stacklet_mcp-0.1.0/stacklet/mcp/assetdb/__init__.py +4 -0
- stacklet_mcp-0.1.0/stacklet/mcp/assetdb/models.py +268 -0
- stacklet_mcp-0.1.0/stacklet/mcp/assetdb/redash.py +268 -0
- stacklet_mcp-0.1.0/stacklet/mcp/assetdb/sql_info.md +147 -0
- stacklet_mcp-0.1.0/stacklet/mcp/assetdb/tools.py +404 -0
- stacklet_mcp-0.1.0/stacklet/mcp/cmdline.py +67 -0
- stacklet_mcp-0.1.0/stacklet/mcp/docs/__init__.py +4 -0
- stacklet_mcp-0.1.0/stacklet/mcp/docs/client.py +80 -0
- stacklet_mcp-0.1.0/stacklet/mcp/docs/models.py +33 -0
- stacklet_mcp-0.1.0/stacklet/mcp/docs/tools.py +71 -0
- stacklet_mcp-0.1.0/stacklet/mcp/lifespan.py +52 -0
- stacklet_mcp-0.1.0/stacklet/mcp/mcp.py +23 -0
- stacklet_mcp-0.1.0/stacklet/mcp/mcp_info.md +40 -0
- stacklet_mcp-0.1.0/stacklet/mcp/platform/__init__.py +4 -0
- stacklet_mcp-0.1.0/stacklet/mcp/platform/dataset_info.md +115 -0
- stacklet_mcp-0.1.0/stacklet/mcp/platform/graphql.py +250 -0
- stacklet_mcp-0.1.0/stacklet/mcp/platform/graphql_info.md +71 -0
- stacklet_mcp-0.1.0/stacklet/mcp/platform/models.py +152 -0
- stacklet_mcp-0.1.0/stacklet/mcp/platform/tools.py +240 -0
- stacklet_mcp-0.1.0/stacklet/mcp/server.py +35 -0
- stacklet_mcp-0.1.0/stacklet/mcp/settings.py +42 -0
- stacklet_mcp-0.1.0/stacklet/mcp/stacklet_auth.py +105 -0
- stacklet_mcp-0.1.0/stacklet/mcp/utils/__init__.py +4 -0
- stacklet_mcp-0.1.0/stacklet/mcp/utils/file.py +31 -0
- stacklet_mcp-0.1.0/stacklet/mcp/utils/json.py +67 -0
- stacklet_mcp-0.1.0/stacklet/mcp/utils/mcp_json.py +82 -0
- stacklet_mcp-0.1.0/stacklet/mcp/utils/text.py +14 -0
- stacklet_mcp-0.1.0/stacklet/mcp/utils/tool.py +27 -0
- stacklet_mcp-0.1.0/tests/__init__.py +6 -0
- stacklet_mcp-0.1.0/tests/conftest.py +64 -0
- stacklet_mcp-0.1.0/tests/factory.py +146 -0
- stacklet_mcp-0.1.0/tests/test_cmdline.py +61 -0
- stacklet_mcp-0.1.0/tests/test_settings.py +122 -0
- stacklet_mcp-0.1.0/tests/test_stacklet_auth.py +179 -0
- stacklet_mcp-0.1.0/tests/test_tools_assetdb.py +685 -0
- stacklet_mcp-0.1.0/tests/test_tools_docs.py +100 -0
- stacklet_mcp-0.1.0/tests/test_tools_platform.py +674 -0
- stacklet_mcp-0.1.0/tests/test_utils_mcp_json.py +49 -0
- stacklet_mcp-0.1.0/tests/test_utils_text.py +18 -0
- stacklet_mcp-0.1.0/tests/testing/http.py +140 -0
- stacklet_mcp-0.1.0/tests/testing/mcp.py +76 -0
- stacklet_mcp-0.1.0/tests/testing/settings.py +33 -0
- stacklet_mcp-0.1.0/uv.lock +1380 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
* @stacklet/engineering
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
name: Common setup
|
|
2
|
+
description: common setup for actions
|
|
3
|
+
runs:
|
|
4
|
+
using: composite
|
|
5
|
+
steps:
|
|
6
|
+
- name: Tool versions
|
|
7
|
+
uses: wistia/parse-tool-versions@v2.1.1
|
|
8
|
+
with:
|
|
9
|
+
prefix: TOOL_VERSION_
|
|
10
|
+
|
|
11
|
+
- name: Setup Just
|
|
12
|
+
uses: extractions/setup-just@v3
|
|
13
|
+
with:
|
|
14
|
+
just-version: ${{ env.TOOL_VERSION_JUST }}
|
|
15
|
+
|
|
16
|
+
- name: Setup Python
|
|
17
|
+
uses: actions/setup-python@v5
|
|
18
|
+
with:
|
|
19
|
+
python-version: ${{ env.TOOL_VERSION_PYTHON }}
|
|
20
|
+
|
|
21
|
+
- name: Setup uv
|
|
22
|
+
uses: astral-sh/setup-uv@v6
|
|
23
|
+
with:
|
|
24
|
+
version: ${{ env.TOOL_VERSION_UV }}
|
|
25
|
+
activate-environment: true
|
|
26
|
+
enable-cache: true
|
|
27
|
+
|
|
28
|
+
- name: Install dependencies
|
|
29
|
+
shell: sh
|
|
30
|
+
run: |
|
|
31
|
+
just install
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
name: "CI"
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches:
|
|
6
|
+
- main
|
|
7
|
+
- release/**
|
|
8
|
+
pull_request:
|
|
9
|
+
|
|
10
|
+
jobs:
|
|
11
|
+
lint:
|
|
12
|
+
runs-on: ubuntu-latest
|
|
13
|
+
steps:
|
|
14
|
+
- name: Checkout
|
|
15
|
+
uses: actions/checkout@v5
|
|
16
|
+
|
|
17
|
+
- name: Install
|
|
18
|
+
uses: ./.github/composites/install
|
|
19
|
+
|
|
20
|
+
- name: Lint
|
|
21
|
+
run: |
|
|
22
|
+
just lint
|
|
23
|
+
|
|
24
|
+
test:
|
|
25
|
+
runs-on: ubuntu-latest
|
|
26
|
+
steps:
|
|
27
|
+
- name: Checkout
|
|
28
|
+
uses: actions/checkout@v5
|
|
29
|
+
|
|
30
|
+
- name: Install
|
|
31
|
+
uses: ./.github/composites/install
|
|
32
|
+
|
|
33
|
+
- name: Unit tests
|
|
34
|
+
run: |
|
|
35
|
+
just test-coverage
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
name: Release
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags:
|
|
6
|
+
- "v[0-9]+.[0-9]+.[0-9]+"
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
release:
|
|
10
|
+
runs-on: ubuntu-latest
|
|
11
|
+
environment: pypi
|
|
12
|
+
permissions:
|
|
13
|
+
id-token: write
|
|
14
|
+
steps:
|
|
15
|
+
- name: Checkout
|
|
16
|
+
uses: actions/checkout@v5
|
|
17
|
+
|
|
18
|
+
- name: Install
|
|
19
|
+
uses: ./.github/composites/install
|
|
20
|
+
|
|
21
|
+
- name: Build packages
|
|
22
|
+
run: |
|
|
23
|
+
uv build
|
|
24
|
+
|
|
25
|
+
- name: Publish packges to PyPI
|
|
26
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# Python
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*$py.class
|
|
5
|
+
*.egg-info/
|
|
6
|
+
.coverage
|
|
7
|
+
.coverage.*
|
|
8
|
+
.pytest_cache/
|
|
9
|
+
|
|
10
|
+
# IDE
|
|
11
|
+
.vscode/*
|
|
12
|
+
!.vscode/settings.json
|
|
13
|
+
!.vscode/launch.json
|
|
14
|
+
*.swp
|
|
15
|
+
*.swo
|
|
16
|
+
|
|
17
|
+
# OS
|
|
18
|
+
.DS_Store
|
|
19
|
+
|
|
20
|
+
# Local config
|
|
21
|
+
.mcp.json
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
repos:
|
|
2
|
+
- repo: https://github.com/pre-commit/pre-commit-hooks
|
|
3
|
+
rev: v6.0.0
|
|
4
|
+
hooks:
|
|
5
|
+
- id: trailing-whitespace
|
|
6
|
+
- id: end-of-file-fixer
|
|
7
|
+
- id: debug-statements
|
|
8
|
+
|
|
9
|
+
- repo: https://github.com/astral-sh/ruff-pre-commit
|
|
10
|
+
rev: v0.12.7
|
|
11
|
+
hooks:
|
|
12
|
+
- id: ruff-check
|
|
13
|
+
args: [--fix]
|
|
14
|
+
- id: ruff-format
|
|
15
|
+
|
|
16
|
+
- repo: https://github.com/tox-dev/pyproject-fmt
|
|
17
|
+
rev: v2.6.0
|
|
18
|
+
hooks:
|
|
19
|
+
- id: pyproject-fmt
|
|
20
|
+
|
|
21
|
+
- repo: https://github.com/pre-commit/mirrors-mypy
|
|
22
|
+
rev: v1.13.0
|
|
23
|
+
hooks:
|
|
24
|
+
- id: mypy
|
|
25
|
+
args: [--config-file=pyproject.toml]
|
|
26
|
+
exclude: ^tests/
|
|
27
|
+
# some dependencies don't export types, so they're needed in the mypy
|
|
28
|
+
# virtualenv
|
|
29
|
+
additional_dependencies:
|
|
30
|
+
- fastmcp
|
|
31
|
+
- pydantic
|
|
32
|
+
|
|
33
|
+
- repo: https://github.com/arkinmodi/add-license-header
|
|
34
|
+
rev: v2.4.0
|
|
35
|
+
hooks:
|
|
36
|
+
- id: add-license-header
|
|
37
|
+
args:
|
|
38
|
+
- --single-year-if-same
|
|
39
|
+
- --year-delimiter='-'
|
|
40
|
+
- --license-file=license-header.tpl
|
|
41
|
+
files: ^.*\.py$
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
# CLAUDE.md
|
|
2
|
+
|
|
3
|
+
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
|
4
|
+
|
|
5
|
+
## Project Overview
|
|
6
|
+
|
|
7
|
+
This is an MCP (Model Context Protocol) server that provides comprehensive tools for interacting with the Stacklet platform. The server exposes tools for documentation access, Stacklet's cloud governance GraphQL API, and AssetDB operations (with some tools conditionally enabled based on configuration).
|
|
8
|
+
|
|
9
|
+
## Architecture
|
|
10
|
+
|
|
11
|
+
The codebase follows a modular design with clear separation of concerns:
|
|
12
|
+
|
|
13
|
+
**Core Components:**
|
|
14
|
+
- `stacklet/mcp/server.py` - Server factory that creates the FastMCP instance
|
|
15
|
+
- `stacklet/mcp/mcp.py` - Main entry point with CLI integration
|
|
16
|
+
- `stacklet/mcp/cmdline.py` - Command line interface with agent config generation
|
|
17
|
+
- `stacklet/mcp/stacklet_auth.py` - Authentication credential loading
|
|
18
|
+
- `stacklet/mcp/utils/` - Utility functions package (text, json, mcp_json, tool helpers)
|
|
19
|
+
- `stacklet/mcp/settings.py` - Server configuration and feature flags
|
|
20
|
+
- `stacklet/mcp/lifespan.py` - Application lifespan management
|
|
21
|
+
|
|
22
|
+
**Docs Package:**
|
|
23
|
+
- `stacklet/mcp/docs/client.py` - Documentation client for fetching docs from Stacklet deployment
|
|
24
|
+
- `stacklet/mcp/docs/tools.py` - Documentation tool implementations (docs_list, docs_read)
|
|
25
|
+
- `stacklet/mcp/docs/models.py` - Pydantic models for documentation responses
|
|
26
|
+
|
|
27
|
+
**Platform Package:**
|
|
28
|
+
- `stacklet/mcp/platform/graphql.py` - Platform GraphQL client with instance-level schema caching
|
|
29
|
+
- `stacklet/mcp/platform/tools.py` - Platform tool implementations (platform_graphql_info, platform_graphql_query, platform_dataset_export, etc.)
|
|
30
|
+
- `stacklet/mcp/platform/models.py` - Pydantic models for platform operations (ExportColumn, ExportParam, ConnectionExport, etc.)
|
|
31
|
+
- `stacklet/mcp/platform/graphql_info.md` - Detailed guidance for using the Platform GraphQL API
|
|
32
|
+
- `stacklet/mcp/platform/dataset_info.md` - Guide for exporting large datasets from the Platform API
|
|
33
|
+
|
|
34
|
+
**AssetDB Package:**
|
|
35
|
+
- `stacklet/mcp/assetdb/redash.py` - AssetDB client using Redash API for SQL queries and saved query management
|
|
36
|
+
- `stacklet/mcp/assetdb/models.py` - Pydantic models specific to AssetDB (Query, User, JobStatus, QueryUpsert, etc.)
|
|
37
|
+
- `stacklet/mcp/assetdb/tools.py` - AssetDB tool implementations (assetdb_query_list, assetdb_sql_query, etc.)
|
|
38
|
+
- `stacklet/mcp/assetdb/sql_info.md` - Comprehensive guide to AssetDB structure and querying best practices
|
|
39
|
+
|
|
40
|
+
**Authentication Flow:**
|
|
41
|
+
The authentication system echoes the Stacklet Terraform provider's credential resolution:
|
|
42
|
+
1. Environment variables (`STACKLET_ENDPOINT`, `STACKLET_ACCESS_TOKEN`, `STACKLET_IDENTITY_TOKEN`)
|
|
43
|
+
2. Config files saved by the `stacklet-admin` CLI in `~/.stacklet`
|
|
44
|
+
|
|
45
|
+
Note: `identity_token` is required for AssetDB access via Redash authentication cookies.
|
|
46
|
+
|
|
47
|
+
**Tool Naming Convention:**
|
|
48
|
+
Tools use component-based prefixes:
|
|
49
|
+
- `platform_*` - Tools for Stacklet's platform GraphQL API
|
|
50
|
+
- `assetdb_*` - Tools for asset database operations (SQL queries and saved query management)
|
|
51
|
+
- Pattern: `{component}_{action}` (e.g., `platform_graphql_query`, `assetdb_sql_query`)
|
|
52
|
+
|
|
53
|
+
## Development Commands
|
|
54
|
+
|
|
55
|
+
**Install dependencies:**
|
|
56
|
+
```bash
|
|
57
|
+
uv sync
|
|
58
|
+
# or: just install
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
**Run the MCP server:**
|
|
62
|
+
```bash
|
|
63
|
+
uv run stacklet-mcp
|
|
64
|
+
# or: just run
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
**Development commands (via justfile):**
|
|
68
|
+
```bash
|
|
69
|
+
just install # Install dependencies and generate .mcp.json if needed
|
|
70
|
+
just lint # Run pre-commit hooks (formatters and linters)
|
|
71
|
+
just test # Run pytest with optional args
|
|
72
|
+
just test-coverage # Run tests with coverage reporting
|
|
73
|
+
just inspect # Run MCP inspector tool
|
|
74
|
+
just agent-config # Generate .mcp.json configuration
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
## MCP Tools Available
|
|
79
|
+
|
|
80
|
+
**Documentation Tools:**
|
|
81
|
+
1. **`docs_list`** - List all available Stacklet user documentation files
|
|
82
|
+
2. **`docs_read`** - Read a specific Stacklet documentation file
|
|
83
|
+
|
|
84
|
+
**Platform GraphQL Tools:**
|
|
85
|
+
3. **`platform_graphql_info`** - Key information for LLMs using GraphQL tools (call this first)
|
|
86
|
+
4. **`platform_graphql_list_types`** - List the types available in the GraphQL API
|
|
87
|
+
5. **`platform_graphql_get_types`** - Retrieve information about specific GraphQL types
|
|
88
|
+
6. **`platform_graphql_query`** - Execute GraphQL queries against Stacklet platform
|
|
89
|
+
7. **`platform_dataset_info`** - Guide for exporting large datasets
|
|
90
|
+
8. **`platform_dataset_export`** - Export full datasets from GraphQL connections to CSV format
|
|
91
|
+
9. **`platform_dataset_lookup`** - Check the status of dataset exports
|
|
92
|
+
|
|
93
|
+
**AssetDB Tools:**
|
|
94
|
+
10. **`assetdb_sql_info`** - Key information for LLMs using AssetDB SQL tools (call this first)
|
|
95
|
+
11. **`assetdb_sql_query`** - Execute ad-hoc SQL queries against AssetDB
|
|
96
|
+
12. **`assetdb_query_list`** - List and search saved queries with pagination
|
|
97
|
+
13. **`assetdb_query_get`** - Get detailed information about specific saved queries
|
|
98
|
+
14. **`assetdb_query_result`** - Get results for saved queries with caching control
|
|
99
|
+
15. **`assetdb_query_save`** - Create new queries or update existing ones (conditionally enabled via `STACKLET_MCP_ASSETDB_ALLOW_SAVE=true`)
|
|
100
|
+
16. **`assetdb_query_archive`** - Archive saved queries (conditionally enabled via `STACKLET_MCP_ASSETDB_ALLOW_ARCHIVE=true`)
|
|
101
|
+
|
|
102
|
+
Total: 16 tools (2 docs + 7 platform + 7 assetdb tools, with 2 assetdb tools conditionally enabled)
|
|
103
|
+
|
|
104
|
+
The actual tools available are determined by each package's `tools()` function implementation, with some tools conditionally enabled based on server configuration settings.
|
|
105
|
+
|
|
106
|
+
## Key Implementation Details
|
|
107
|
+
|
|
108
|
+
**Schema Caching:** The `PlatformClient` class implements instance-level caching to avoid repeated introspection queries, improving performance for schema-heavy operations.
|
|
109
|
+
|
|
110
|
+
**Client Management:** Both AssetDB and Platform clients use a `.get(ctx)` pattern for lazy initialization and caching in FastMCP context. Credentials are loaded once per session using `StackletCredentials.get(ctx)`.
|
|
111
|
+
|
|
112
|
+
**Error Handling:** All GraphQL and SQL operations return structured responses, with network errors and JSON parsing errors handled gracefully. AssetDB supports async query polling for long-running operations.
|
|
113
|
+
|
|
114
|
+
**Credential Security:** Access tokens and identity tokens are never logged or exposed in error messages.
|
|
115
|
+
|
|
116
|
+
**Platform Integration:** Uses GraphQL API for Stacklet platform operations. The Platform package is organized into:
|
|
117
|
+
- `graphql.py` - Core GraphQL client with schema caching and introspection
|
|
118
|
+
- `tools.py` - FastMCP tool implementations that expose platform functionality (info, list types, get types, query, dataset exports)
|
|
119
|
+
- `models.py` - Pydantic models for exports and GraphQL operations
|
|
120
|
+
Uses `graphql-core` for schema manipulation and SDL generation, enabling proper type introspection and schema documentation.
|
|
121
|
+
|
|
122
|
+
**AssetDB Integration:** Uses Redash API for SQL query execution and saved query management. The AssetDB package is organized into:
|
|
123
|
+
- `redash.py` - Core client with async operations and authentication
|
|
124
|
+
- `models.py` - Pydantic models for Redash API responses (Query, User, JobStatus, etc.)
|
|
125
|
+
- `tools.py` - FastMCP tool implementations that expose AssetDB functionality
|
|
126
|
+
Supports query timeouts (max 300s), pagination, search, and tag filtering.
|
|
127
|
+
|
|
128
|
+
## Configuration
|
|
129
|
+
|
|
130
|
+
**Stacklet Credentials:**
|
|
131
|
+
The server requires Stacklet credentials configured through one of:
|
|
132
|
+
- Environment variables: `STACKLET_ENDPOINT`, `STACKLET_ACCESS_TOKEN`, and `STACKLET_IDENTITY_TOKEN`
|
|
133
|
+
- CLI config: `~/.stacklet/config.json` (endpoint), `~/.stacklet/credentials` (access token), and `~/.stacklet/id` (identity token)
|
|
134
|
+
|
|
135
|
+
**Server Settings:**
|
|
136
|
+
Additional configuration via environment variables with `STACKLET_MCP_` prefix:
|
|
137
|
+
- `STACKLET_MCP_DOWNLOADS_PATH` (default: system temp directory) - Directory for storing query result files
|
|
138
|
+
- `STACKLET_MCP_ASSETDB_DATASOURCE` (default: 1) - AssetDB data source ID
|
|
139
|
+
- `STACKLET_MCP_ASSETDB_ALLOW_SAVE` (default: false) - Enable query save/update functionality
|
|
140
|
+
- `STACKLET_MCP_ASSETDB_ALLOW_ARCHIVE` (default: false) - Enable query archiving functionality
|
|
141
|
+
- `STACKLET_MCP_PLATFORM_ALLOW_MUTATIONS` (default: false) - Enable calling mutations in the Platform GraphQL API
|
|
142
|
+
|
|
143
|
+
**File Storage:**
|
|
144
|
+
Query results from AssetDB tools are automatically saved to the configured downloads directory:
|
|
145
|
+
- Complete query results are saved as JSON files for analysis with other tools
|
|
146
|
+
- Files use descriptive naming: `assetdb_{query_id}_{result_id}.json` (for saved queries) or `assetdb_{result_id}.json` (for ad-hoc queries)
|
|
147
|
+
- The downloads directory is created automatically if it doesn't exist
|
|
148
|
+
- Files persist after tool execution for further analysis
|
|
149
|
+
|
|
150
|
+
**External Dependencies:**
|
|
151
|
+
- Documentation files are fetched from the live Stacklet docs service at runtime
|
|
152
|
+
- Redash endpoint is derived by replacing "api." with "redash." in the platform endpoint
|
|
153
|
+
- Docs endpoint is derived by replacing "api." with "docs." in the platform endpoint
|
|
154
|
+
|
|
155
|
+
## Known Issues & Design Notes
|
|
156
|
+
|
|
157
|
+
When you're editing code which matches one of these concerns, think extra hard about
|
|
158
|
+
the impact of your changes; prefer to mitigate these issues rather than further
|
|
159
|
+
entrench them.
|
|
160
|
+
|
|
161
|
+
**Documentation Service Dependency:** The docs client fetches documentation from a live Stacklet deployment's docs service (derived by replacing "api." with "docs." in the platform endpoint). This requires proper authentication and network access to the docs service.
|
|
162
|
+
|
|
163
|
+
**AssetDB Data Source:** The AssetDB client defaults to `data_source_id=1` for the main AssetDB instance. This is hardcoded but can be overridden in function calls
|
|
164
|
+
internally.
|
|
165
|
+
|
|
166
|
+
**Authentication Complexity:** Requires three different credential fields (endpoint, access_token, identity_token) which must all be configured correctly for full functionality.
|
|
167
|
+
|
|
168
|
+
**Test Coverage:** Most tests are end-to-end tool tests with mocked downstream HTTP
|
|
169
|
+
interactions, which is an appropriate level of abstraction for most cases in this codebase. AssetDB in particular is lacking test coverage in some areas, but most tools are well tested, and the patterns seen in the existing tests should be repeated where possible.
|
|
170
|
+
|
|
171
|
+
**Dict Returns:** Many tools and client methods should return better-structured data.
|
|
172
|
+
|
|
173
|
+
**Loose Validation:** Tool parameters in particular could often benefit from further
|
|
174
|
+
type annotation to encode (and advertise to clients!) expectations. Obvious examples include:
|
|
175
|
+
- download_format literals
|
|
176
|
+
- assetdb result timeout
|
|
177
|
+
- query_id >= 1
|
|
178
|
+
|
|
179
|
+
## Agent Configuration
|
|
180
|
+
|
|
181
|
+
**MCP Client Configuration:**
|
|
182
|
+
The server supports generating `.mcp.json` configuration files for Claude Desktop and other MCP clients:
|
|
183
|
+
|
|
184
|
+
```bash
|
|
185
|
+
# List available profiles
|
|
186
|
+
just run agent-config list
|
|
187
|
+
|
|
188
|
+
# Generate configuration for default profile (restricted)
|
|
189
|
+
just run agent-config generate default
|
|
190
|
+
|
|
191
|
+
# Generate configuration for unrestricted profile (all tools enabled)
|
|
192
|
+
just run agent-config generate unrestricted
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
**Configuration Profiles:**
|
|
196
|
+
- `default` - Safe profile with write operations disabled (assetdb_allow_save=false, assetdb_allow_archive=false, platform_allow_mutations=false)
|
|
197
|
+
- `unrestricted` - Full access profile with all tools enabled
|
|
198
|
+
|
|
199
|
+
## Important Advice
|
|
200
|
+
|
|
201
|
+
- When running python in this project, always use "uv run python".
|
|
202
|
+
- When you've made code changes, verify them with "just test" and "just lint".
|
|
203
|
+
- Use `just install` to set up the development environment and generate `.mcp.json` automatically.
|
|
204
|
+
- You will find it useful to have access to the Redash source code as you work; this
|
|
205
|
+
project talks to `https://github.com/stacklet/redash`, NOT the upstream project by
|
|
206
|
+
`getredash`. Cloning that repository into a temp directory and using the filesystem
|
|
207
|
+
is the most effective way to answer questions about redash implementation details.
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: stacklet-mcp
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: MCP server for the Stacklet environment
|
|
5
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
6
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
7
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
8
|
+
Requires-Python: >=3.12
|
|
9
|
+
Requires-Dist: fastmcp>=2
|
|
10
|
+
Requires-Dist: graphql-core>=3.2
|
|
11
|
+
Requires-Dist: httpx>=0.28.1
|
|
12
|
+
Requires-Dist: pydantic-settings>=2.10.1
|
|
13
|
+
Requires-Dist: pydantic>=2.11.7
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
# Stacklet MCP Server
|
|
2
|
+
|
|
3
|
+
This is the MCP (Model Context Protocol) server for the Stacklet environment.
|
|
4
|
+
It exposes toolsets for granting LLMs the powers of:
|
|
5
|
+
|
|
6
|
+
* AssetDB SQL queries (ad-hoc and saved)
|
|
7
|
+
* Platform GraphQL operations (and export of large datasets)
|
|
8
|
+
* Documentation access (in .md, for context)
|
|
9
|
+
|
|
10
|
+
Each of the toolsets has an "info" tool with useful context, available to any client which supports minimal MCP features.
|
|
11
|
+
Asking your agent to tell you about a toolset is a fairly reliable way to get it to call the info tool and load up its context with relevant information.
|
|
12
|
+
|
|
13
|
+
**WARNING**: most Stacklet installations contain confidential data; this server largely exists to feed that data to your agent's LLM, which is generally running in a datacenter _Somewhere_. Be very confident in your understanding of all relevant data security policies before running this server.
|
|
14
|
+
|
|
15
|
+
**FURTHER WARNING**: when running with default settings, the worst an LLM can do with this server is make ill-advised queries, which is often regrettable but generally no worse than a well-intentioned human user might do.
|
|
16
|
+
|
|
17
|
+
By enabling the `_ALLOW_` options documented below, you are granting an LLM free rein to do _anything_ you could do with Stacklet; doing so naturally bears risks proportional to your own level of access. Strongly consider authenticating as a custom Stacklet user with permissions tightly scoped to your task as an additional safeguard.
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
## Installation
|
|
21
|
+
|
|
22
|
+
The easiest way to get the `stacklet-mcp` binary is via `pip` or `uv`, with
|
|
23
|
+
|
|
24
|
+
```
|
|
25
|
+
pip install stacklet-mcp
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
or
|
|
29
|
+
|
|
30
|
+
```
|
|
31
|
+
uv tool install stacklet-mcp
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Once installed, an agent configuration file (`.mcp.json`) can be generated with
|
|
35
|
+
|
|
36
|
+
```
|
|
37
|
+
stacklet-mcp agent-config generate $profile
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
where `$profile` can be either default (no edit allowed) or `unrestricted` (all edits allowed).
|
|
41
|
+
The configuration can be manually tweaked to just allow some edits.
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
## Authentication
|
|
45
|
+
|
|
46
|
+
The MCP server needs to be authenticated wtih the Stacklet environment before use.
|
|
47
|
+
The easiest way to authenticate to your Stacklet environment is to use the [stacklet-admin](https://pypi.org/project/stacklet.client.platform/) command, which is most easily installed with:
|
|
48
|
+
|
|
49
|
+
```
|
|
50
|
+
uv tool install stacklet.client.platform
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Once you've configured that, a
|
|
54
|
+
|
|
55
|
+
```
|
|
56
|
+
stacklet-admin login
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
will grant the MCP server access as the authenticated user for twelve hours. Leaving aside the default blocks on saving queries and mutating platform, the server will have the same powers and restrictions as that user.
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
## Server configuration
|
|
63
|
+
|
|
64
|
+
The MCP server can be configured via environment variables.
|
|
65
|
+
|
|
66
|
+
When the MCP is run from an agent, those can be set in the `"env"` section of the `.mcp.json` file.
|
|
67
|
+
|
|
68
|
+
The following variables are available:
|
|
69
|
+
|
|
70
|
+
- `STACKLET_MCP_DOWNLOADS_PATH`: directory for storing query result files (default: system temp directory)
|
|
71
|
+
- `STACKLET_MCP_ASSETDB_DATASOURCE`: the datasource ID for AssetDB in Redash (default: `1`)
|
|
72
|
+
- `STACKLET_MCP_ASSETDB_ALLOW_SAVE`: whether to enable write operations in AssetDB (default: `false`)
|
|
73
|
+
- `STACKLET_MCP_ASSETDB_ALLOW_ARCHIVE`: whether to enable query archiving functionality in AssetDB (default: `false`)
|
|
74
|
+
- `STACKLET_MCP_PLATFORM_ALLOW_MUTATIONS`: whether to enable executing mutations in Platform API (default: `false`)
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
## Development
|
|
78
|
+
|
|
79
|
+
For development, a few setup steps are required:
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
1) install required tools. The easiest way is through [Mise](https://mise.jdx.dev/):
|
|
83
|
+
|
|
84
|
+
```
|
|
85
|
+
mise install
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
2) install dependencies via
|
|
89
|
+
|
|
90
|
+
```
|
|
91
|
+
just install
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
This will also create a default`.mcp.json` (so long as there's nothing there already) with default read-only settings, which enables convenient experimentation by running e.g. [Claude Code](https://claude.com/product/claude-code)) in the project root without risk of altering saved AssetDB queries or running Platform mutations.
|
|
95
|
+
|
|
96
|
+
For alternative integrations, the `.mcp.json` file should serve as a starting point, but the details may vary by context; the best documentation for the format itself seems to be [here](https://gofastmcp.com/integrations/mcp-json-configuration#mcp-json-configuration-standard).
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
3) Authenticate to the deployment as described above.
|
|
100
|
+
|
|
101
|
+
4) Run the MCP server with
|
|
102
|
+
|
|
103
|
+
```
|
|
104
|
+
just run [options...]
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
### Inspect MCP protocol
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
The MCP Protocol Inspector is invaluable for peeking at the details of the protocol in use. It can be run via
|
|
112
|
+
|
|
113
|
+
```
|
|
114
|
+
just inspect
|
|
115
|
+
```
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# Setup development environment
|
|
2
|
+
install:
|
|
3
|
+
#!/usr/bin/env bash
|
|
4
|
+
set -e
|
|
5
|
+
|
|
6
|
+
uv sync
|
|
7
|
+
if [ ! -e .mcp.json ]; then
|
|
8
|
+
just agent-config
|
|
9
|
+
echo "Wrote .mcp.json"
|
|
10
|
+
fi
|
|
11
|
+
|
|
12
|
+
# Run the MCP server locally
|
|
13
|
+
run *args:
|
|
14
|
+
uv run stacklet-mcp {{args}}
|
|
15
|
+
|
|
16
|
+
# Run code formatters/linters
|
|
17
|
+
lint:
|
|
18
|
+
uv run pre-commit run --all-files
|
|
19
|
+
|
|
20
|
+
# Run tests
|
|
21
|
+
test *args:
|
|
22
|
+
uv run pytest {{args}}
|
|
23
|
+
|
|
24
|
+
# Run tests with coverage
|
|
25
|
+
test-coverage *args:
|
|
26
|
+
just test --cov {{args}}
|
|
27
|
+
|
|
28
|
+
# Run mcp-inspector
|
|
29
|
+
inspect:
|
|
30
|
+
npx @modelcontextprotocol/inspector just run
|
|
31
|
+
|
|
32
|
+
# Generate agent configuration (.mcp.json)
|
|
33
|
+
agent-config profile="default":
|
|
34
|
+
just run agent-config generate {{profile}} > .mcp.json
|
|
35
|
+
|
|
36
|
+
# Tag current commit with a release tag based on the project version
|
|
37
|
+
tag-release:
|
|
38
|
+
#!/usr/bin/env bash
|
|
39
|
+
set -e
|
|
40
|
+
|
|
41
|
+
version="$(uv run python -c 'from stacklet.mcp import __version__; print(__version__)')"
|
|
42
|
+
git tag -a "v$version" -m "Version $version"
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
Copyright (c) $create_year$year_delimiter$edit_year Stacklet, Inc.
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
build-backend = "hatchling.build"
|
|
3
|
+
|
|
4
|
+
requires = [ "hatchling" ]
|
|
5
|
+
|
|
6
|
+
[project]
|
|
7
|
+
name = "stacklet-mcp"
|
|
8
|
+
description = "MCP server for the Stacklet environment"
|
|
9
|
+
requires-python = ">=3.12"
|
|
10
|
+
classifiers = [
|
|
11
|
+
"Programming Language :: Python :: 3 :: Only",
|
|
12
|
+
"Programming Language :: Python :: 3.12",
|
|
13
|
+
"Programming Language :: Python :: 3.13",
|
|
14
|
+
]
|
|
15
|
+
dynamic = [ "version" ]
|
|
16
|
+
|
|
17
|
+
dependencies = [
|
|
18
|
+
"fastmcp>=2",
|
|
19
|
+
"graphql-core>=3.2",
|
|
20
|
+
"httpx>=0.28.1",
|
|
21
|
+
"pydantic>=2.11.7",
|
|
22
|
+
"pydantic-settings>=2.10.1",
|
|
23
|
+
]
|
|
24
|
+
scripts.stacklet-mcp = "stacklet.mcp.mcp:main"
|
|
25
|
+
|
|
26
|
+
[dependency-groups]
|
|
27
|
+
dev = [
|
|
28
|
+
"add-license-header>=2.4",
|
|
29
|
+
"mypy>=1.8",
|
|
30
|
+
"pre-commit>=4.2",
|
|
31
|
+
"pyproject-fmt>=2.6",
|
|
32
|
+
"pytest>=7",
|
|
33
|
+
"pytest-asyncio>=0.21",
|
|
34
|
+
"pytest-cov>=7",
|
|
35
|
+
"ruff>=0.12",
|
|
36
|
+
]
|
|
37
|
+
|
|
38
|
+
[tool.hatch.version]
|
|
39
|
+
path = "stacklet/mcp/__init__.py"
|
|
40
|
+
|
|
41
|
+
[tool.hatch.build.targets.wheel]
|
|
42
|
+
packages = [ "stacklet" ]
|
|
43
|
+
|
|
44
|
+
[tool.ruff]
|
|
45
|
+
line-length = 100
|
|
46
|
+
|
|
47
|
+
# Add `line-too-long` and isort to the enforced rule set.
|
|
48
|
+
lint.extend-select = [ "E501", "I" ]
|
|
49
|
+
lint.isort.lines-after-imports = 2
|
|
50
|
+
# Use a single line between direct and from import.
|
|
51
|
+
lint.isort.lines-between-types = 1
|
|
52
|
+
|
|
53
|
+
[tool.pytest.ini_options]
|
|
54
|
+
asyncio_mode = "auto"
|
|
55
|
+
addopts = "--no-cov-on-fail"
|
|
56
|
+
|
|
57
|
+
[tool.coverage.report]
|
|
58
|
+
fail_under = 94.0
|
|
59
|
+
show_missing = true
|
|
60
|
+
skip_covered = true
|
|
61
|
+
|
|
62
|
+
[tool.mypy]
|
|
63
|
+
strict = true
|
|
64
|
+
install_types = true
|
|
65
|
+
warn_return_any = true
|
|
66
|
+
warn_unused_configs = true
|
|
67
|
+
check_untyped_defs = true
|
|
68
|
+
warn_redundant_casts = true
|
|
69
|
+
warn_unused_ignores = true
|
|
70
|
+
warn_no_return = true
|
|
71
|
+
ignore_missing_imports = true
|
|
72
|
+
non_interactive = true
|
|
73
|
+
exclude = [ "tests/" ]
|
|
74
|
+
plugins = [ "pydantic.mypy" ]
|