konokenj.cdk-api-mcp-server 0.0.1__py3-none-any.whl → 0.1.0__py3-none-any.whl

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.

Potentially problematic release.


This version of konokenj.cdk-api-mcp-server might be problematic. Click here for more details.

@@ -1,4 +1,4 @@
1
1
  # SPDX-FileCopyrightText: 2025-present Kenji Kono <konoken@amazon.co.jp>
2
2
  #
3
3
  # SPDX-License-Identifier: MIT
4
- __version__ = "0.0.1"
4
+ __version__ = "0.1.0"
@@ -2,6 +2,6 @@
2
2
  #
3
3
  # SPDX-License-Identifier: MIT
4
4
 
5
- from .__about__ import __version__
5
+ from cdk_api_mcp_server.__about__ import __version__
6
6
 
7
7
  __all__ = ["__version__"]
@@ -0,0 +1,24 @@
1
+ """AWS CDK API MCP model definitions."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import List, Optional
6
+
7
+ from pydantic import BaseModel, Field
8
+
9
+
10
+ class FileItem(BaseModel):
11
+ """File item model for CDK API documentation."""
12
+
13
+ name: str = Field(description="Name of the file")
14
+ uri: str = Field(description="URI of the file")
15
+ is_directory: bool = Field(description="Whether the item is a directory")
16
+
17
+
18
+ class FileList(BaseModel):
19
+ """List of files in CDK API documentation."""
20
+
21
+ files: List[FileItem] = Field(default_factory=list, description="List of files")
22
+ error: Optional[str] = Field(
23
+ default=None, description="Error message if files not found"
24
+ )
@@ -0,0 +1,161 @@
1
+ """AWS CDK API MCP resource handlers."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import json
6
+ import logging
7
+ from abc import ABC, abstractmethod
8
+ from importlib.resources import files
9
+ from pathlib import Path
10
+ from typing import List
11
+
12
+ # Set up logging
13
+ logger = logging.getLogger(__name__)
14
+
15
+
16
+ class ResourceProvider(ABC):
17
+ """リソースプロバイダーのインターフェース"""
18
+
19
+ @abstractmethod
20
+ def get_resource_content(self, path: str) -> str:
21
+ """リソースの内容を取得する"""
22
+
23
+ @abstractmethod
24
+ def list_resources(self, path: str) -> List[str]:
25
+ """指定パスのリソース一覧を取得する"""
26
+
27
+ @abstractmethod
28
+ def resource_exists(self, path: str) -> bool:
29
+ """リソースが存在するかチェックする"""
30
+
31
+
32
+ class PackageResourceProvider(ResourceProvider):
33
+ """Pythonパッケージからリソースを提供するプロバイダー"""
34
+
35
+ def __init__(self, package_name: str = "cdk_api_mcp_server"):
36
+ self.package_name = package_name
37
+
38
+ def get_resource_content(self, path: str) -> str:
39
+ """新しいimportlib.resourcesのAPIを使用してパッケージからリソースを読み込む"""
40
+ try:
41
+ # 末尾のパス部分を分離(ディレクトリとファイル名)
42
+ parts = path.strip("/").split("/")
43
+
44
+ if len(parts) < 1:
45
+ return "Error: Invalid resource path"
46
+
47
+ # パスが"constructs"で始まる場合は"aws-cdk/constructs"に変換
48
+ if parts[0] == "constructs":
49
+ parts[0] = "aws-cdk/constructs"
50
+
51
+ # resources配下の実際のファイルへのパスを構築
52
+ base_path = files(self.package_name).joinpath("resources")
53
+
54
+ # パス要素を結合
55
+ resource_path = base_path
56
+ for part in parts[0].split("/"):
57
+ resource_path = resource_path.joinpath(part)
58
+
59
+ for part in parts[1:]:
60
+ resource_path = resource_path.joinpath(part)
61
+
62
+ # ファイルとして存在するかチェック
63
+ if resource_path.is_file():
64
+ return resource_path.read_text(encoding="utf-8")
65
+ elif resource_path.is_dir():
66
+ # ディレクトリの場合は内容一覧を返す
67
+ dir_contents = [entry.name for entry in resource_path.iterdir()]
68
+ return f"Directory: {path}\nContents: {', '.join(sorted(dir_contents))}"
69
+ else:
70
+ return f"Error: Resource '{path}' not found"
71
+ except (FileNotFoundError, IsADirectoryError, PermissionError) as e:
72
+ logger.debug(f"リソースの取得に失敗: {path} - {e}")
73
+ return f"Error: Resource '{path}' not found - {e!s}"
74
+
75
+ def list_resources(self, path: str) -> List[str]:
76
+ """新しいimportlib.resourcesのAPIを使用してパッケージ内のリソース一覧を取得する"""
77
+ try:
78
+ # resources配下の実際のディレクトリへのパスを構築
79
+ base_path = files(self.package_name).joinpath("resources")
80
+
81
+ # パス要素を結合
82
+ resource_path = base_path
83
+ if path:
84
+ parts = path.strip("/").split("/")
85
+ # パスが"constructs"で始まる場合は"aws-cdk/constructs"に変換
86
+ if parts[0] == "constructs":
87
+ parts[0] = "aws-cdk/constructs"
88
+
89
+ # パスの最初の部分("aws-cdk/constructs"を含む)を分割して追加
90
+ for part in parts[0].split("/"):
91
+ resource_path = resource_path.joinpath(part)
92
+
93
+ # 残りのパス要素を追加
94
+ for part in parts[1:]:
95
+ resource_path = resource_path.joinpath(part)
96
+
97
+ # ディレクトリとして存在するかチェック
98
+ if resource_path.is_dir():
99
+ items = sorted([entry.name for entry in resource_path.iterdir()])
100
+
101
+ # クライアントに返すときはハイフン形式に変換
102
+ if path and path.startswith("constructs/") and not path.endswith("/"):
103
+ # ディレクトリでないパスの場合、モジュール名として扱いハイフン形式にする
104
+ return [item.replace("_", "-") for item in items]
105
+ return items
106
+ else:
107
+ return []
108
+ except (FileNotFoundError, NotADirectoryError, PermissionError) as e:
109
+ logger.debug(f"リソース一覧の取得に失敗: {path} - {e}")
110
+ return []
111
+
112
+ def resource_exists(self, path: str) -> bool:
113
+ """新しいimportlib.resourcesのAPIを使用してリソースが存在するかチェックする"""
114
+ try:
115
+ # resources配下の実際のファイルへのパスを構築
116
+ base_path = files(self.package_name).joinpath("resources")
117
+
118
+ # パス要素を結合
119
+ resource_path = base_path
120
+ if path:
121
+ parts = path.strip("/").split("/")
122
+ # パスが"constructs"で始まる場合は"aws-cdk/constructs"に変換
123
+ if parts[0] == "constructs":
124
+ parts[0] = "aws-cdk/constructs"
125
+
126
+ # パスの最初の部分("aws-cdk/constructs"を含む)を分割して追加
127
+ for part in parts[0].split("/"):
128
+ resource_path = resource_path.joinpath(part)
129
+
130
+ # 残りのパス要素を追加
131
+ for part in parts[1:]:
132
+ resource_path = resource_path.joinpath(part)
133
+
134
+ # 存在チェック - Traversableには.exists()がないので、is_file()とis_dir()で確認
135
+ return resource_path.is_file() or resource_path.is_dir()
136
+ except (FileNotFoundError, PermissionError) as e:
137
+ logger.debug(f"リソースの存在チェックに失敗: {path} - {e}")
138
+ return False
139
+
140
+
141
+ # Define resource directories for backward compatibility with tests
142
+ CONSTRUCTS_DIR = Path(__file__).parent / "resources" / "aws-cdk" / "constructs"
143
+
144
+
145
+ def get_package_content(provider: ResourceProvider, package_name: str) -> str:
146
+ """Get content for a package resource as a simple JSON array.
147
+
148
+ Args:
149
+ provider: Resource provider
150
+ package_name: Package name
151
+
152
+ Returns:
153
+ JSON array of module names as a string
154
+ """
155
+ resource_path = f"constructs/{package_name}"
156
+
157
+ if not provider.resource_exists(resource_path):
158
+ return "[]" # 空の配列を返す
159
+ else:
160
+ modules = provider.list_resources(resource_path)
161
+ return json.dumps(modules)
@@ -1,8 +1,217 @@
1
- #!/usr/bin/env python3
2
1
  """AWS CDK API MCP server implementation."""
3
2
 
4
- from cdk_api_mcp_server.core.server import main
3
+ import json
4
+ import mimetypes
5
+ import os
6
+ from typing import Optional
5
7
 
8
+ from fastmcp import FastMCP
9
+ from fastmcp.resources import TextResource
10
+ from pydantic import AnyUrl
6
11
 
7
- if __name__ == '__main__':
12
+ from cdk_api_mcp_server.resources import (
13
+ PackageResourceProvider,
14
+ ResourceProvider,
15
+ get_package_content,
16
+ )
17
+
18
+ # MIMEタイプの初期化と追加
19
+ mimetypes.init()
20
+ mimetypes.add_type("text/markdown", ".md")
21
+ mimetypes.add_type("text/typescript", ".ts")
22
+ mimetypes.add_type("application/json", ".json")
23
+
24
+ # デフォルトのMCPサーバーインスタンス
25
+ mcp: FastMCP = FastMCP()
26
+ # デフォルトのリソースプロバイダー
27
+ _default_provider = PackageResourceProvider()
28
+
29
+
30
+ def create_server(provider: Optional[ResourceProvider] = None) -> FastMCP:
31
+ """Create an MCP server with the given resource provider.
32
+
33
+ Args:
34
+ provider: ResourceProvider for CDK API resources. Defaults to PackageResourceProvider.
35
+
36
+ Returns:
37
+ FastMCP server instance with registered resources
38
+ """
39
+ # 使用するリソースプロバイダー
40
+ resource_provider = provider or _default_provider
41
+
42
+ # 新しいサーバーインスタンスを作成
43
+ server: FastMCP = FastMCP()
44
+
45
+ # nameフィールドにアクセスする方法は提供されていないため、descriptionを使う
46
+ server.description = "AWS CDK API MCP Server"
47
+
48
+ # 定義済みのパッケージとして直接リソース登録
49
+ @server.resource("cdk-api-docs://constructs/@aws-cdk", mime_type="application/json")
50
+ def get_aws_cdk_alpha_packages():
51
+ """Get AWS CDK Alpha modules published in @aws-cdk namespace."""
52
+ content = get_package_content(resource_provider, "@aws-cdk")
53
+
54
+ # JSONとしてレスポンスを返す
55
+ return TextResource(
56
+ uri=AnyUrl.build(
57
+ scheme="cdk-api-docs", host="constructs", path="/@aws-cdk"
58
+ ),
59
+ name="@aws-cdk",
60
+ text=content,
61
+ description="AWS CDK Alpha modules",
62
+ mime_type="application/json", # JSONレスポンスのMIMEタイプを設定
63
+ )
64
+
65
+ @server.resource(
66
+ "cdk-api-docs://constructs/aws-cdk-lib", mime_type="application/json"
67
+ )
68
+ def get_aws_cdk_lib_packages():
69
+ """Get AWS CDK Stable modules in aws-cdk-lib package."""
70
+ content = get_package_content(resource_provider, "aws-cdk-lib")
71
+
72
+ # JSONとしてレスポンスを返す
73
+ return TextResource(
74
+ uri=AnyUrl.build(
75
+ scheme="cdk-api-docs", host="constructs", path="/aws-cdk-lib"
76
+ ),
77
+ name="aws-cdk-lib",
78
+ text=content,
79
+ description="AWS CDK Stable modules",
80
+ mime_type="application/json", # JSONレスポンスのMIMEタイプを設定
81
+ )
82
+
83
+ # リソーステンプレート:パッケージ内のモジュール一覧
84
+ @server.resource(
85
+ "cdk-api-docs://constructs/{package_name}/", mime_type="application/json"
86
+ )
87
+ def list_package_modules(package_name: str):
88
+ """List all modules in a package."""
89
+ modules = [
90
+ item
91
+ for item in resource_provider.list_resources(f"constructs/{package_name}")
92
+ if resource_provider.resource_exists(f"constructs/{package_name}/{item}/")
93
+ ]
94
+ content = json.dumps(modules)
95
+
96
+ # JSONとしてレスポンスを返す
97
+ return TextResource(
98
+ uri=AnyUrl.build(
99
+ scheme="cdk-api-docs", host="constructs", path=f"/{package_name}/"
100
+ ),
101
+ name=f"{package_name}-modules",
102
+ text=content,
103
+ description=f"Modules in {package_name}",
104
+ mime_type="application/json", # JSONレスポンスのMIMEタイプを設定
105
+ )
106
+
107
+ # リソーステンプレート:モジュール内のファイル一覧
108
+ @server.resource(
109
+ "cdk-api-docs://constructs/{package_name}/{module_name}/",
110
+ mime_type="application/json",
111
+ )
112
+ def list_module_files(package_name: str, module_name: str):
113
+ """List all files in a module."""
114
+ files = resource_provider.list_resources(
115
+ f"constructs/{package_name}/{module_name}"
116
+ )
117
+ content = json.dumps(files)
118
+
119
+ # JSONとしてレスポンスを返す
120
+ return TextResource(
121
+ uri=AnyUrl.build(
122
+ scheme="cdk-api-docs",
123
+ host="constructs",
124
+ path=f"/{package_name}/{module_name}/",
125
+ ),
126
+ name=f"{module_name}-files",
127
+ text=content,
128
+ description=f"Files in {package_name}/{module_name}",
129
+ mime_type="application/json", # JSONレスポンスのMIMEタイプを設定
130
+ )
131
+
132
+ # リソーステンプレート:すべてのファイルタイプ対応
133
+ @server.resource(
134
+ "cdk-api-docs://constructs/{package_name}/{module_name}/{file_name}"
135
+ )
136
+ def get_construct_file(package_name: str, module_name: str, file_name: str):
137
+ """Get a file from the constructs directory."""
138
+ resource_path = f"constructs/{package_name}/{module_name}/{file_name}"
139
+
140
+ if not resource_provider.resource_exists(resource_path):
141
+ error_message = f"Error: File '{resource_path}' not found"
142
+ return TextResource(
143
+ uri=AnyUrl.build(
144
+ scheme="cdk-api-docs",
145
+ host="constructs",
146
+ path=f"/{package_name}/{module_name}/{file_name}",
147
+ ),
148
+ name=file_name,
149
+ text=error_message,
150
+ description=f"Error: {resource_path}",
151
+ mime_type="text/plain",
152
+ )
153
+
154
+ # リソースプロバイダーからコンテンツを取得
155
+ content = resource_provider.get_resource_content(resource_path)
156
+
157
+ # 拡張子に基づいてMIMEタイプを決定
158
+ _, ext = os.path.splitext(file_name)
159
+
160
+ # 特定の拡張子に対して明示的にMIMEタイプを設定
161
+ if ext == ".md":
162
+ mime_type = "text/markdown"
163
+ description = f"Markdown file: {package_name}/{module_name}/{file_name}"
164
+ elif ext == ".ts":
165
+ mime_type = "text/typescript"
166
+ description = f"TypeScript file: {package_name}/{module_name}/{file_name}"
167
+ elif ext == ".json":
168
+ mime_type = "application/json"
169
+ description = f"JSON file: {package_name}/{module_name}/{file_name}"
170
+ else:
171
+ # その他の場合はmimetypesモジュールで判定
172
+ mime_type, _ = mimetypes.guess_type(file_name)
173
+ if mime_type is None:
174
+ # デフォルトはプレーンテキスト
175
+ mime_type = "text/plain"
176
+ description = f"File: {package_name}/{module_name}/{file_name}"
177
+
178
+ # Create and return a TextResource
179
+ return TextResource(
180
+ uri=AnyUrl.build(
181
+ scheme="cdk-api-docs",
182
+ host="constructs",
183
+ path=f"/{package_name}/{module_name}/{file_name}",
184
+ ),
185
+ name=file_name,
186
+ text=content,
187
+ description=description,
188
+ mime_type=mime_type,
189
+ )
190
+
191
+ return server
192
+
193
+
194
+ # デフォルトのサーバーを初期化
195
+ def initialize_default_server() -> None:
196
+ """Initialize the default MCP server with resources."""
197
+ global mcp
198
+ default_server = create_server(_default_provider)
199
+ # 以前のmcpの属性を新しいサーバーにコピー
200
+ mcp.__dict__.update(default_server.__dict__)
201
+
202
+
203
+ # デフォルトサーバーを初期化
204
+ initialize_default_server()
205
+
206
+
207
+ def main():
208
+ """Run the MCP server."""
209
+ import logging
210
+
211
+ logging.basicConfig(level=logging.INFO)
212
+ # サーバーを実行
213
+ mcp.run()
214
+
215
+
216
+ if __name__ == "__main__":
8
217
  main()
@@ -0,0 +1,79 @@
1
+ Metadata-Version: 2.4
2
+ Name: konokenj.cdk-api-mcp-server
3
+ Version: 0.1.0
4
+ Summary: An MCP server provides AWS CDK API Reference
5
+ Project-URL: Documentation, https://github.com/konokenj/cdk-api-mcp-server#readme
6
+ Project-URL: Issues, https://github.com/konokenj/cdk-api-mcp-server/issues
7
+ Project-URL: Source, https://github.com/konokenj/cdk-api-mcp-server
8
+ Author-email: Kenji Kono <konoken@amazon.co.jp>
9
+ License-Expression: MIT
10
+ License-File: LICENSE.txt
11
+ Classifier: Development Status :: 4 - Beta
12
+ Classifier: Programming Language :: Python
13
+ Classifier: Programming Language :: Python :: 3.8
14
+ Classifier: Programming Language :: Python :: 3.9
15
+ Classifier: Programming Language :: Python :: 3.10
16
+ Classifier: Programming Language :: Python :: 3.11
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Classifier: Programming Language :: Python :: Implementation :: CPython
19
+ Classifier: Programming Language :: Python :: Implementation :: PyPy
20
+ Requires-Python: >=3.8
21
+ Requires-Dist: fastmcp>=2.0.0
22
+ Requires-Dist: pydantic>=2.10.6
23
+ Provides-Extra: dev
24
+ Requires-Dist: mypy; extra == 'dev'
25
+ Requires-Dist: pygithub; extra == 'dev'
26
+ Requires-Dist: semantic-version; extra == 'dev'
27
+ Description-Content-Type: text/markdown
28
+
29
+ # CDK API MCP Server
30
+
31
+ [![PyPI - Version](https://img.shields.io/pypi/v/konokenj.cdk-api-mcp-server.svg)](https://pypi.org/project/konokenj.cdk-api-mcp-server)
32
+ [![PyPI - Python Version](https://img.shields.io/pypi/pyversions/konokenj.cdk-api-mcp-server.svg)](https://pypi.org/project/konokenj.cdk-api-mcp-server)
33
+
34
+ ---
35
+
36
+ ## Usage
37
+
38
+ Add to your mcp.json:
39
+
40
+ ```json
41
+ {
42
+ "mcpServers": {
43
+ "konokenj.cdk-api-mcp-server": {
44
+ "command": "uvx",
45
+ "args": ["konokenj.cdk-api-mcp-server@latest"]
46
+ }
47
+ }
48
+ }
49
+ ```
50
+
51
+ ## MCP Server Capabilities
52
+
53
+ ### Resource: CDK API packages
54
+
55
+ Registered as static resources. To get available modules under the package, call `list_resources()` as MCP client.
56
+
57
+ - `cdk-api-docs://constructs/@aws-cdk` ... Alpha modules published in `@aws-cdk` namespace
58
+ - `cdk-api-docs://constructs/aws-cdk-lib` ... Stable modules in `aws-cdk-lib` package
59
+
60
+ ### Resource Template: List modules in package
61
+
62
+ To get available documents under the module, call `read_resource(uri)` as MCP client.
63
+
64
+ > [!Note]
65
+ > Chagne first and second part of uri as you need.
66
+
67
+ - `cdk-api-docs://constructs/@aws-cdk/{module}/`
68
+ - `cdk-api-docs://constructs/aws-cdk-lib/{module}/`
69
+
70
+ ### Resource Template: Read file contents
71
+
72
+ To read a document, call `read_resource(uri)` as MCP client.
73
+
74
+ - `cdk-api-docs://constructs/@aws-cdk/{module}/{file}`
75
+ - `cdk-api-docs://constructs/aws-cdk-lib/{module}/{file}`
76
+
77
+ ## License
78
+
79
+ Distributed under the terms of the [MIT](https://spdx.org/licenses/MIT.html) license.
@@ -0,0 +1,11 @@
1
+ cdk_api_mcp_server/__about__.py,sha256=B-Tsyc49A_y_VAjgIZQF5Fu-eiw_14QHDdGq0Tcc6E0,128
2
+ cdk_api_mcp_server/__init__.py,sha256=yJA6yIEhJviC-qNlB-nC6UR1JblQci_d84i-viHZkc0,187
3
+ cdk_api_mcp_server/models.py,sha256=cMS1Hi29M41YjuBxqqrzNrNvyG3MgnUBb1SqYpMCJ30,692
4
+ cdk_api_mcp_server/resources.py,sha256=R7LVwn29I4BJzU5XAwKbX8j6uy-3ZxcB1b0HzZ_Z2PI,6689
5
+ cdk_api_mcp_server/server.py,sha256=QTJ_e1sDU4Z64hinJFgApCwA2tKt_ma21jgRFw7ZBdw,7836
6
+ cdk_api_mcp_server/resources/.gitkeep,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
7
+ konokenj_cdk_api_mcp_server-0.1.0.dist-info/METADATA,sha256=5wAzy9IjpoGMq9E0ohLDo3JTZ0TyPbhi0FK-brLTuqs,2678
8
+ konokenj_cdk_api_mcp_server-0.1.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
9
+ konokenj_cdk_api_mcp_server-0.1.0.dist-info/entry_points.txt,sha256=bVDhMdyCC1WNMPOMbmB82jvWII2CIrwTZDygdCf0cYQ,79
10
+ konokenj_cdk_api_mcp_server-0.1.0.dist-info/licenses/LICENSE.txt,sha256=5OIAASeg1HM22mVZ1enz9bgZ7TlsGfWXnj02P9OgFyk,1098
11
+ konokenj_cdk_api_mcp_server-0.1.0.dist-info/RECORD,,
@@ -1 +0,0 @@
1
- """Core module for CDK API MCP server."""
@@ -1,121 +0,0 @@
1
- #!/usr/bin/env python3
2
- """AWS CDK API MCP resource handlers."""
3
-
4
- import logging
5
- import os
6
- from pathlib import Path
7
- from typing import Optional
8
-
9
-
10
- # Set up logging
11
- logger = logging.getLogger(__name__)
12
-
13
-
14
- # Define resource directories
15
- DOCS_DIR = Path(__file__).parent.parent / "resources" / "aws-cdk" / "docs"
16
- INTEG_TESTS_DIR = Path(__file__).parent.parent / "resources" / "aws-cdk" / "integ-tests"
17
-
18
-
19
- async def get_cdk_api_docs(category: str, package_name: str, module_name: str, file_path: str) -> str:
20
- """Get AWS CDK API documentation from the resources directory.
21
-
22
- This resource handler serves documentation files from the resources/aws-cdk/docs directory.
23
- The files are organized by category, package and module.
24
-
25
- Example URIs:
26
- - cdk-api-docs://packages/@aws-cdk/aws-s3/README.md
27
- - cdk-api-docs://packages/aws-cdk-lib/aws-lambda/README.md
28
- - cdk-api-docs://root/DEPRECATED_APIs.md
29
-
30
- Args:
31
- category: The category (e.g., 'packages', 'root')
32
- package_name: The package name (e.g., '@aws-cdk', 'aws-cdk-lib')
33
- module_name: The module name (e.g., 'aws-s3', 'aws-lambda')
34
- file_path: The file path within the module (e.g., 'README.md')
35
-
36
- Returns:
37
- String containing the requested documentation
38
- """
39
- # Handle special case for root files like DEPRECATED_APIs.md
40
- if category == "root":
41
- file_path = os.path.join(DOCS_DIR, package_name)
42
- if os.path.exists(file_path):
43
- with open(file_path, 'r', encoding='utf-8') as f:
44
- return f.read()
45
- else:
46
- return f"Error: File '{package_name}' not found"
47
-
48
- # For packages category, construct the file path
49
- if category == "packages":
50
- if file_path:
51
- file_path = os.path.join(DOCS_DIR, category, package_name, module_name, file_path)
52
- else:
53
- file_path = os.path.join(DOCS_DIR, category, package_name, module_name)
54
- else:
55
- # For other categories, construct the path accordingly
56
- if file_path:
57
- file_path = os.path.join(DOCS_DIR, category, package_name, module_name, file_path)
58
- else:
59
- file_path = os.path.join(DOCS_DIR, category, package_name, module_name)
60
-
61
- # Check if the file exists
62
- if os.path.exists(file_path):
63
- # If it's a directory, list the contents
64
- if os.path.isdir(file_path):
65
- files = os.listdir(file_path)
66
- result = f"# Contents of {package_name}/{module_name}\n\n"
67
- for f in sorted(files):
68
- if os.path.isdir(os.path.join(file_path, f)):
69
- result += f"- [{f}/](cdk-api-docs://{category}/{package_name}/{module_name}/{f})\n"
70
- else:
71
- result += f"- [{f}](cdk-api-docs://{category}/{package_name}/{module_name}/{f})\n"
72
- return result
73
- # If it's a file, return the contents
74
- else:
75
- with open(file_path, 'r', encoding='utf-8') as f:
76
- return f.read()
77
- else:
78
- return f"Error: File '{file_path}' not found"
79
-
80
-
81
- async def get_cdk_api_integ_tests(module_name: str, file_path: Optional[str] = None) -> str:
82
- """Get AWS CDK integration test examples from the resources directory.
83
-
84
- This resource handler serves integration test files from the resources/aws-cdk/integ-tests directory.
85
- The files are organized by module.
86
-
87
- Example URIs:
88
- - cdk-api-integ-tests://aws-s3/aws-s3.test1.md
89
- - cdk-api-integ-tests://aws-lambda/aws-lambda.handler.md
90
-
91
- Args:
92
- module_name: The module name (e.g., 'aws-s3', 'aws-lambda')
93
- file_path: The file path within the module (e.g., 'aws-s3.test1.md')
94
-
95
- Returns:
96
- String containing the requested integration test example
97
- """
98
- # Construct the file path
99
- if file_path:
100
- file_path = os.path.join(INTEG_TESTS_DIR, module_name, file_path)
101
- else:
102
- file_path = os.path.join(INTEG_TESTS_DIR, module_name)
103
-
104
- # Check if the file exists
105
- if os.path.exists(file_path):
106
- # If it's a directory, list the contents
107
- if os.path.isdir(file_path):
108
- files = os.listdir(file_path)
109
- result = f"# Integration Tests for {module_name}\n\n"
110
- for f in sorted(files):
111
- if os.path.isdir(os.path.join(file_path, f)):
112
- result += f"- [{f}/](cdk-api-integ-tests://{module_name}/{f})\n"
113
- else:
114
- result += f"- [{f}](cdk-api-integ-tests://{module_name}/{f})\n"
115
- return result
116
- # If it's a file, return the contents
117
- else:
118
- with open(file_path, 'r', encoding='utf-8') as f:
119
- return f.read()
120
- else:
121
- return f"Error: File '{file_path}' not found"
@@ -1,255 +0,0 @@
1
- #!/usr/bin/env python3
2
- """AWS CDK API MCP server implementation."""
3
-
4
- import logging
5
- import os
6
- import json
7
- from pathlib import Path
8
- from cdk_api_mcp_server.core import resources
9
- from fastmcp import FastMCP
10
- from fastmcp.resources import TextResource, DirectoryResource
11
-
12
-
13
- # Set up logging
14
- logger = logging.getLogger(__name__)
15
-
16
-
17
- # Define resource directories
18
- DOCS_DIR = Path(__file__).parent.parent / "resources" / "aws-cdk" / "docs"
19
- INTEG_TESTS_DIR = Path(__file__).parent.parent / "resources" / "aws-cdk" / "integ-tests"
20
-
21
-
22
- # Create MCP server
23
- mcp = FastMCP(
24
- 'AWS CDK API MCP Server',
25
- dependencies=[],
26
- )
27
-
28
-
29
- # Register resource templates for hierarchical navigation
30
- @mcp.resource('cdk-api-docs://')
31
- async def list_root_categories():
32
- """List all available categories in the CDK API documentation."""
33
- if not DOCS_DIR.exists():
34
- return {"error": "Documentation directory not found"}
35
-
36
- categories = []
37
- # Add root category
38
- categories.append({
39
- "name": "root",
40
- "uri": "cdk-api-docs://root/",
41
- "description": "Root level documentation files",
42
- "is_directory": True
43
- })
44
-
45
- # Add packages category if it exists
46
- packages_dir = DOCS_DIR / "packages"
47
- if packages_dir.exists() and packages_dir.is_dir():
48
- categories.append({
49
- "name": "packages",
50
- "uri": "cdk-api-docs://packages/",
51
- "description": "AWS CDK packages documentation",
52
- "is_directory": True
53
- })
54
-
55
- return json.dumps({"categories": categories})
56
-
57
-
58
- @mcp.resource('cdk-api-docs://root/')
59
- def list_root_files():
60
- """List all files in the root directory of the CDK API documentation."""
61
- if not DOCS_DIR.exists():
62
- return {"error": "Documentation directory not found"}
63
-
64
- files = []
65
- for item in DOCS_DIR.iterdir():
66
- if item.is_file():
67
- files.append({
68
- "name": item.name,
69
- "uri": f"cdk-api-docs://root/{item.name}",
70
- "is_directory": False
71
- })
72
- elif item.is_dir() and item.name != "packages": # Skip packages dir as it's handled separately
73
- files.append({
74
- "name": item.name,
75
- "uri": f"cdk-api-docs://root/{item.name}/",
76
- "is_directory": True
77
- })
78
-
79
- return json.dumps({"files": files})
80
-
81
-
82
- @mcp.resource('cdk-api-docs://root/{file_name}')
83
- def get_root_file(file_name: str):
84
- """Get a file from the root directory of the CDK API documentation."""
85
- file_path = DOCS_DIR / file_name
86
-
87
- if not file_path.exists() or not file_path.is_file():
88
- return f"Error: File '{file_name}' not found"
89
-
90
- # Read the file content
91
- with open(file_path, 'r', encoding='utf-8') as f:
92
- content = f.read()
93
-
94
- # Create and return a TextResource
95
- return TextResource(
96
- uri=f"cdk-api-docs://root/{file_name}",
97
- name=file_name,
98
- text=content,
99
- description=f"Root documentation file: {file_name}",
100
- mime_type="text/markdown" if file_name.endswith(".md") else "text/plain"
101
- )
102
-
103
-
104
- @mcp.resource('cdk-api-docs://packages/')
105
- def list_packages():
106
- """List all packages in the CDK API documentation."""
107
- packages_dir = DOCS_DIR / "packages"
108
-
109
- if not packages_dir.exists() or not packages_dir.is_dir():
110
- return {"error": "Packages directory not found"}
111
-
112
- packages = []
113
- for item in packages_dir.iterdir():
114
- if item.is_dir():
115
- packages.append({
116
- "name": item.name,
117
- "uri": f"cdk-api-docs://packages/{item.name}/",
118
- "is_directory": True
119
- })
120
-
121
- return json.dumps({"packages": packages})
122
-
123
-
124
- @mcp.resource('cdk-api-docs://packages/{package_name}/')
125
- def list_package_modules(package_name: str):
126
- """List all modules in a specific package."""
127
- package_dir = DOCS_DIR / "packages" / package_name
128
-
129
- if not package_dir.exists() or not package_dir.is_dir():
130
- return {"error": f"Package '{package_name}' not found"}
131
-
132
- modules = []
133
- for item in package_dir.iterdir():
134
- if item.is_dir():
135
- modules.append({
136
- "name": item.name,
137
- "uri": f"cdk-api-docs://packages/{package_name}/{item.name}/",
138
- "is_directory": True
139
- })
140
- elif item.is_file():
141
- modules.append({
142
- "name": item.name,
143
- "uri": f"cdk-api-docs://packages/{package_name}/{item.name}",
144
- "is_directory": False
145
- })
146
-
147
- return json.dumps({"modules": modules})
148
-
149
-
150
- @mcp.resource('cdk-api-docs://packages/{package_name}/{module_name}/')
151
- def list_module_files(package_name: str, module_name: str):
152
- """List all files in a specific module."""
153
- module_dir = DOCS_DIR / "packages" / package_name / module_name
154
-
155
- if not module_dir.exists() or not module_dir.is_dir():
156
- return {"error": f"Module '{module_name}' not found in package '{package_name}'"}
157
-
158
- # ここでのみDirectoryResourceを使用
159
- return DirectoryResource(
160
- uri=f"cdk-api-docs://packages/{package_name}/{module_name}/",
161
- name=f"Files in {package_name}/{module_name}",
162
- path=module_dir,
163
- description=f"List of files in the {package_name}/{module_name} module",
164
- recursive=False
165
- )
166
-
167
-
168
- @mcp.resource('cdk-api-docs://packages/{package_name}/{module_name}/{file_path}')
169
- def get_module_file(package_name: str, module_name: str, file_path: str):
170
- """Get a specific file from a module."""
171
- file_full_path = DOCS_DIR / "packages" / package_name / module_name / file_path
172
-
173
- if not file_full_path.exists() or not file_full_path.is_file():
174
- return f"Error: File '{file_path}' not found in {package_name}/{module_name}"
175
-
176
- # Read the file content
177
- with open(file_full_path, 'r', encoding='utf-8') as f:
178
- content = f.read()
179
-
180
- # Create and return a TextResource
181
- return TextResource(
182
- uri=f"cdk-api-docs://packages/{package_name}/{module_name}/{file_path}",
183
- name=file_path,
184
- text=content,
185
- description=f"Documentation file: {file_path} in {package_name}/{module_name}",
186
- mime_type="text/markdown" if file_path.endswith(".md") else "text/plain"
187
- )
188
-
189
-
190
- # Register integration tests resources
191
- @mcp.resource('cdk-api-integ-tests://')
192
- def list_integ_test_modules():
193
- """List all modules with integration tests."""
194
- if not INTEG_TESTS_DIR.exists():
195
- return {"error": "Integration tests directory not found"}
196
-
197
- modules = []
198
- for item in INTEG_TESTS_DIR.iterdir():
199
- if item.is_dir():
200
- modules.append({
201
- "name": item.name,
202
- "uri": f"cdk-api-integ-tests://{item.name}/",
203
- "is_directory": True
204
- })
205
-
206
- return json.dumps({"modules": modules})
207
-
208
-
209
- @mcp.resource('cdk-api-integ-tests://{module_name}/')
210
- def list_module_tests(module_name: str):
211
- """List all integration tests for a specific module."""
212
- module_dir = INTEG_TESTS_DIR / module_name
213
-
214
- if not module_dir.exists() or not module_dir.is_dir():
215
- return {"error": f"Module '{module_name}' not found in integration tests"}
216
-
217
- # ここでのみDirectoryResourceを使用
218
- return DirectoryResource(
219
- uri=f"cdk-api-integ-tests://{module_name}/",
220
- name=f"Integration tests for {module_name}",
221
- path=module_dir,
222
- description=f"List of integration tests for the {module_name} module",
223
- recursive=False
224
- )
225
-
226
-
227
- @mcp.resource('cdk-api-integ-tests://{module_name}/{file_path}')
228
- def get_module_test(module_name: str, file_path: str):
229
- """Get a specific integration test file."""
230
- file_full_path = INTEG_TESTS_DIR / module_name / file_path
231
-
232
- if not file_full_path.exists() or not file_full_path.is_file():
233
- return f"Error: Integration test '{file_path}' not found for module '{module_name}'"
234
-
235
- # Read the file content
236
- with open(file_full_path, 'r', encoding='utf-8') as f:
237
- content = f.read()
238
-
239
- # Create and return a TextResource
240
- return TextResource(
241
- uri=f"cdk-api-integ-tests://{module_name}/{file_path}",
242
- name=file_path,
243
- text=content,
244
- description=f"Integration test: {file_path} for {module_name}",
245
- mime_type="text/markdown" if file_path.endswith(".md") else "text/plain"
246
- )
247
-
248
-
249
- def main():
250
- """Run the MCP server with CLI argument support."""
251
- mcp.run()
252
-
253
-
254
- if __name__ == '__main__':
255
- main()
@@ -1,45 +0,0 @@
1
- Metadata-Version: 2.4
2
- Name: konokenj.cdk-api-mcp-server
3
- Version: 0.0.1
4
- Summary: An MCP server provides AWS CDK API Reference
5
- Project-URL: Documentation, https://github.com/konokenj/cdk-api-mcp-server#readme
6
- Project-URL: Issues, https://github.com/konokenj/cdk-api-mcp-server/issues
7
- Project-URL: Source, https://github.com/konokenj/cdk-api-mcp-server
8
- Author-email: Kenji Kono <konoken@amazon.co.jp>
9
- License-Expression: MIT
10
- License-File: LICENSE.txt
11
- Classifier: Development Status :: 4 - Beta
12
- Classifier: Programming Language :: Python
13
- Classifier: Programming Language :: Python :: 3.8
14
- Classifier: Programming Language :: Python :: 3.9
15
- Classifier: Programming Language :: Python :: 3.10
16
- Classifier: Programming Language :: Python :: 3.11
17
- Classifier: Programming Language :: Python :: 3.12
18
- Classifier: Programming Language :: Python :: Implementation :: CPython
19
- Classifier: Programming Language :: Python :: Implementation :: PyPy
20
- Requires-Python: >=3.8
21
- Requires-Dist: fastmcp>=2.0.0
22
- Requires-Dist: pydantic>=2.10.6
23
- Description-Content-Type: text/markdown
24
-
25
- # cdk-api-mcp-server
26
-
27
- [![PyPI - Version](https://img.shields.io/pypi/v/cdk-api-mcp-server.svg)](https://pypi.org/project/cdk-api-mcp-server)
28
- [![PyPI - Python Version](https://img.shields.io/pypi/pyversions/cdk-api-mcp-server.svg)](https://pypi.org/project/cdk-api-mcp-server)
29
-
30
- -----
31
-
32
- ## Table of Contents
33
-
34
- - [Installation](#installation)
35
- - [License](#license)
36
-
37
- ## Installation
38
-
39
- ```console
40
- pip install cdk-api-mcp-server
41
- ```
42
-
43
- ## License
44
-
45
- `cdk-api-mcp-server` is distributed under the terms of the [MIT](https://spdx.org/licenses/MIT.html) license.
@@ -1,12 +0,0 @@
1
- cdk_api_mcp_server/__about__.py,sha256=YDFhVrJ6IAPLvkmnNQeYbddiqs-wE-itcAwTO84Lt2U,128
2
- cdk_api_mcp_server/__init__.py,sha256=NEvU-xodn9HlOjf2mMHp_TfU-_2UkrV6zJRJSy5vt74,169
3
- cdk_api_mcp_server/server.py,sha256=XAG7UI1_Uy0F5Ff9qoL2z3FhLt5C9he39oHjhEYPhc4,157
4
- cdk_api_mcp_server/core/__init__.py,sha256=vDq0hUSCu3Tfj4EJyp5p40Ak5W68S4y_NkN5Z3pKAkA,42
5
- cdk_api_mcp_server/core/resources.py,sha256=yG0MPNAIYmnXioV3lSI3T1QkeelX5l83MddsbzKCZvs,4723
6
- cdk_api_mcp_server/core/server.py,sha256=KgCycgr7Yxnx4JeZHIk7oG1pz7YaXc1TOva9CclM5B4,8513
7
- cdk_api_mcp_server/resources/.gitkeep,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
8
- konokenj_cdk_api_mcp_server-0.0.1.dist-info/METADATA,sha256=y6E_ViH9EUcZMI0kvT7xz7grYG78aURNGUzGX1Aefr4,1601
9
- konokenj_cdk_api_mcp_server-0.0.1.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
10
- konokenj_cdk_api_mcp_server-0.0.1.dist-info/entry_points.txt,sha256=bVDhMdyCC1WNMPOMbmB82jvWII2CIrwTZDygdCf0cYQ,79
11
- konokenj_cdk_api_mcp_server-0.0.1.dist-info/licenses/LICENSE.txt,sha256=5OIAASeg1HM22mVZ1enz9bgZ7TlsGfWXnj02P9OgFyk,1098
12
- konokenj_cdk_api_mcp_server-0.0.1.dist-info/RECORD,,