@musashishao/agent-kit 1.0.0 → 1.1.0

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 @musashishao/agent-kit might be problematic. Click here for more details.

@@ -0,0 +1,522 @@
1
+ # Python MCP Server Template (FastMCP)
2
+
3
+ > Copy this template to quickly start a Python MCP server.
4
+
5
+ ## Project Structure
6
+
7
+ ```
8
+ my-mcp-server/
9
+ ├── src/
10
+ │ ├── __init__.py
11
+ │ ├── server.py # Entry point
12
+ │ ├── tools/ # Tool implementations
13
+ │ │ ├── __init__.py
14
+ │ │ └── example.py
15
+ │ ├── resources/ # Resource handlers
16
+ │ │ └── __init__.py
17
+ │ └── utils/ # Utilities
18
+ │ └── __init__.py
19
+ ├── tests/
20
+ │ └── test_tools.py
21
+ ├── pyproject.toml
22
+ ├── requirements.txt
23
+ └── README.md
24
+ ```
25
+
26
+ ## Files
27
+
28
+ ### pyproject.toml
29
+
30
+ ```toml
31
+ [project]
32
+ name = "my-mcp-server"
33
+ version = "1.0.0"
34
+ description = "My MCP Server"
35
+ requires-python = ">=3.10"
36
+ dependencies = [
37
+ "fastmcp>=0.3.0",
38
+ "httpx>=0.27.0",
39
+ "pydantic>=2.0.0",
40
+ ]
41
+
42
+ [project.optional-dependencies]
43
+ dev = [
44
+ "pytest>=8.0.0",
45
+ "pytest-asyncio>=0.23.0",
46
+ "ruff>=0.4.0",
47
+ ]
48
+
49
+ [project.scripts]
50
+ my-mcp-server = "src.server:main"
51
+
52
+ [build-system]
53
+ requires = ["hatchling"]
54
+ build-backend = "hatchling.build"
55
+
56
+ [tool.ruff]
57
+ line-length = 100
58
+ target-version = "py310"
59
+
60
+ [tool.pytest.ini_options]
61
+ asyncio_mode = "auto"
62
+ ```
63
+
64
+ ### requirements.txt
65
+
66
+ ```
67
+ fastmcp>=0.3.0
68
+ httpx>=0.27.0
69
+ pydantic>=2.0.0
70
+ python-dotenv>=1.0.0
71
+ ```
72
+
73
+ ### src/server.py
74
+
75
+ ```python
76
+ #!/usr/bin/env python3
77
+ """
78
+ My MCP Server - Main entry point
79
+ """
80
+
81
+ import os
82
+ import logging
83
+ from fastmcp import FastMCP
84
+
85
+ # Import tools and resources
86
+ from .tools.example import register_example_tools
87
+ from .resources import register_resources
88
+
89
+ # Configuration
90
+ SERVER_NAME = "my-mcp-server"
91
+ SERVER_VERSION = "1.0.0"
92
+
93
+ # Setup logging
94
+ logging.basicConfig(
95
+ level=logging.INFO,
96
+ format="%(asctime)s - %(name)s - %(levelname)s - %(message)s"
97
+ )
98
+ logger = logging.getLogger(SERVER_NAME)
99
+
100
+ # Create server instance
101
+ mcp = FastMCP(
102
+ SERVER_NAME,
103
+ version=SERVER_VERSION,
104
+ description="My awesome MCP server"
105
+ )
106
+
107
+ # Register tools and resources
108
+ register_example_tools(mcp)
109
+ register_resources(mcp)
110
+
111
+
112
+ def main():
113
+ """Entry point for the server."""
114
+ logger.info(f"Starting {SERVER_NAME} v{SERVER_VERSION}")
115
+ mcp.run()
116
+
117
+
118
+ if __name__ == "__main__":
119
+ main()
120
+ ```
121
+
122
+ ### src/tools/__init__.py
123
+
124
+ ```python
125
+ """Tool exports."""
126
+
127
+ from .example import register_example_tools
128
+
129
+ __all__ = ["register_example_tools"]
130
+ ```
131
+
132
+ ### src/tools/example.py
133
+
134
+ ```python
135
+ """Example tools for the MCP server."""
136
+
137
+ import json
138
+ from typing import Optional
139
+ from datetime import datetime
140
+ from fastmcp import FastMCP
141
+
142
+
143
+ def register_example_tools(mcp: FastMCP):
144
+ """Register example tools with the server."""
145
+
146
+ @mcp.tool()
147
+ def greet(
148
+ name: str,
149
+ language: str = "en"
150
+ ) -> str:
151
+ """Greet a user by name.
152
+
153
+ Args:
154
+ name: The name of the user to greet
155
+ language: Language for greeting (en, es, fr)
156
+
157
+ Returns:
158
+ A greeting message
159
+ """
160
+ greetings = {
161
+ "en": f"Hello, {name}!",
162
+ "es": f"¡Hola, {name}!",
163
+ "fr": f"Bonjour, {name}!",
164
+ }
165
+ return greetings.get(language, greetings["en"])
166
+
167
+ @mcp.tool()
168
+ def calculate(
169
+ operation: str,
170
+ a: float,
171
+ b: float
172
+ ) -> dict:
173
+ """Perform a mathematical calculation.
174
+
175
+ Args:
176
+ operation: One of 'add', 'subtract', 'multiply', 'divide'
177
+ a: First number
178
+ b: Second number
179
+
180
+ Returns:
181
+ Result of the calculation
182
+ """
183
+ operations = {
184
+ "add": lambda: a + b,
185
+ "subtract": lambda: a - b,
186
+ "multiply": lambda: a * b,
187
+ "divide": lambda: a / b if b != 0 else None,
188
+ }
189
+
190
+ if operation not in operations:
191
+ return {"error": f"Unknown operation: {operation}"}
192
+
193
+ result = operations[operation]()
194
+
195
+ if result is None:
196
+ return {"error": "Division by zero"}
197
+
198
+ return {
199
+ "operation": operation,
200
+ "a": a,
201
+ "b": b,
202
+ "result": result
203
+ }
204
+
205
+ @mcp.tool()
206
+ def get_timestamp() -> dict:
207
+ """Get current timestamp information.
208
+
209
+ Returns:
210
+ Current date/time in various formats
211
+ """
212
+ now = datetime.now()
213
+ return {
214
+ "iso": now.isoformat(),
215
+ "timestamp": int(now.timestamp()),
216
+ "formatted": now.strftime("%Y-%m-%d %H:%M:%S"),
217
+ "date": now.strftime("%Y-%m-%d"),
218
+ "time": now.strftime("%H:%M:%S"),
219
+ }
220
+
221
+ @mcp.tool()
222
+ def json_format(
223
+ data: str,
224
+ indent: int = 2
225
+ ) -> str:
226
+ """Format JSON string with pretty printing.
227
+
228
+ Args:
229
+ data: JSON string to format
230
+ indent: Number of spaces for indentation
231
+
232
+ Returns:
233
+ Formatted JSON string
234
+ """
235
+ try:
236
+ parsed = json.loads(data)
237
+ return json.dumps(parsed, indent=indent, ensure_ascii=False)
238
+ except json.JSONDecodeError as e:
239
+ return f"Error parsing JSON: {e}"
240
+ ```
241
+
242
+ ### src/resources/__init__.py
243
+
244
+ ```python
245
+ """Resource handlers for the MCP server."""
246
+
247
+ import json
248
+ import os
249
+ from datetime import datetime
250
+ from fastmcp import FastMCP
251
+
252
+
253
+ def register_resources(mcp: FastMCP):
254
+ """Register resources with the server."""
255
+
256
+ @mcp.resource("status://server")
257
+ def get_status() -> str:
258
+ """Get server status information."""
259
+ return json.dumps({
260
+ "status": "running",
261
+ "timestamp": datetime.now().isoformat(),
262
+ "pid": os.getpid(),
263
+ "python_version": os.sys.version,
264
+ })
265
+
266
+ @mcp.resource("config://settings")
267
+ def get_config() -> str:
268
+ """Get server configuration."""
269
+ return json.dumps({
270
+ "name": "my-mcp-server",
271
+ "version": "1.0.0",
272
+ "features": ["tools", "resources", "prompts"],
273
+ "environment": os.environ.get("ENVIRONMENT", "development"),
274
+ })
275
+
276
+ @mcp.resource("env://variables")
277
+ def get_safe_env() -> str:
278
+ """Get safe environment variables (non-sensitive)."""
279
+ safe_keys = ["PATH", "HOME", "USER", "SHELL", "LANG"]
280
+ safe_env = {
281
+ k: os.environ.get(k, "")
282
+ for k in safe_keys
283
+ if k in os.environ
284
+ }
285
+ return json.dumps(safe_env)
286
+ ```
287
+
288
+ ### src/utils/__init__.py
289
+
290
+ ```python
291
+ """Utility functions for the MCP server."""
292
+
293
+ import os
294
+ import json
295
+ import hashlib
296
+ from typing import Any, Optional, TypeVar
297
+ from datetime import datetime
298
+ import logging
299
+
300
+ logger = logging.getLogger(__name__)
301
+
302
+ T = TypeVar("T")
303
+
304
+
305
+ def require_env(name: str) -> str:
306
+ """Get required environment variable or raise error."""
307
+ value = os.environ.get(name)
308
+ if not value:
309
+ raise ValueError(f"Missing required environment variable: {name}")
310
+ return value
311
+
312
+
313
+ def get_env(name: str, default: str = "") -> str:
314
+ """Get environment variable with default."""
315
+ return os.environ.get(name, default)
316
+
317
+
318
+ def safe_json_parse(data: str, default: T = None) -> T:
319
+ """Safely parse JSON with fallback."""
320
+ try:
321
+ return json.loads(data)
322
+ except json.JSONDecodeError:
323
+ return default
324
+
325
+
326
+ def generate_id() -> str:
327
+ """Generate a unique ID."""
328
+ timestamp = datetime.now().isoformat()
329
+ return hashlib.sha256(timestamp.encode()).hexdigest()[:12]
330
+
331
+
332
+ def truncate(text: str, max_length: int = 100) -> str:
333
+ """Truncate text with ellipsis."""
334
+ if len(text) <= max_length:
335
+ return text
336
+ return text[:max_length - 3] + "..."
337
+
338
+
339
+ def log_tool_call(tool_name: str, args: dict, result: Any) -> None:
340
+ """Log tool calls for debugging."""
341
+ logger.info(f"Tool: {tool_name}, Args: {args}, Result type: {type(result).__name__}")
342
+ ```
343
+
344
+ ### tests/test_tools.py
345
+
346
+ ```python
347
+ """Tests for MCP tools."""
348
+
349
+ import pytest
350
+ from src.tools.example import register_example_tools
351
+ from fastmcp import FastMCP
352
+
353
+
354
+ @pytest.fixture
355
+ def mcp():
356
+ """Create a test MCP server."""
357
+ server = FastMCP("test-server")
358
+ register_example_tools(server)
359
+ return server
360
+
361
+
362
+ def test_greet_english():
363
+ """Test greeting in English."""
364
+ from src.tools.example import register_example_tools
365
+
366
+ mcp = FastMCP("test")
367
+ register_example_tools(mcp)
368
+
369
+ # Tools are registered, you would call them through the server
370
+ # This is a simplified example
371
+ assert True # Replace with actual test
372
+
373
+
374
+ def test_calculate_add():
375
+ """Test addition calculation."""
376
+ # Import the functions directly for unit testing
377
+ result = {"operation": "add", "a": 2, "b": 3, "result": 5}
378
+ assert result["result"] == 5
379
+
380
+
381
+ def test_calculate_divide_by_zero():
382
+ """Test division by zero handling."""
383
+ result = {"error": "Division by zero"}
384
+ assert "error" in result
385
+ ```
386
+
387
+ ## Running the Server
388
+
389
+ ### Development
390
+
391
+ ```bash
392
+ # Install dependencies
393
+ pip install -r requirements.txt
394
+
395
+ # Or with uv (faster)
396
+ uv pip install -r requirements.txt
397
+
398
+ # Run in development mode
399
+ fastmcp dev src/server.py
400
+
401
+ # Run with hot reload
402
+ fastmcp dev src/server.py --reload
403
+ ```
404
+
405
+ ### Production
406
+
407
+ ```bash
408
+ # Install as package
409
+ pip install .
410
+
411
+ # Run
412
+ my-mcp-server
413
+
414
+ # Or directly
415
+ python -m src.server
416
+ ```
417
+
418
+ ### Claude Desktop Configuration
419
+
420
+ Add to `~/Library/Application Support/Claude/claude_desktop_config.json`:
421
+
422
+ ```json
423
+ {
424
+ "mcpServers": {
425
+ "my-server": {
426
+ "command": "python",
427
+ "args": ["-m", "src.server"],
428
+ "cwd": "/path/to/my-mcp-server",
429
+ "env": {
430
+ "PYTHONPATH": "/path/to/my-mcp-server",
431
+ "MY_API_KEY": "your-api-key"
432
+ }
433
+ }
434
+ }
435
+ }
436
+ ```
437
+
438
+ Or using fastmcp:
439
+
440
+ ```json
441
+ {
442
+ "mcpServers": {
443
+ "my-server": {
444
+ "command": "fastmcp",
445
+ "args": ["run", "/path/to/my-mcp-server/src/server.py"]
446
+ }
447
+ }
448
+ }
449
+ ```
450
+
451
+ ## FastMCP Installation Commands
452
+
453
+ ```bash
454
+ # Install directly to Claude Desktop
455
+ fastmcp install src/server.py --name "My Server"
456
+
457
+ # Install with environment variables
458
+ fastmcp install src/server.py --name "My Server" \
459
+ --env MY_API_KEY=secret \
460
+ --env DATABASE_URL=postgres://...
461
+ ```
462
+
463
+ ## Testing
464
+
465
+ ```bash
466
+ # Run tests
467
+ pytest
468
+
469
+ # Run with coverage
470
+ pytest --cov=src
471
+
472
+ # Test with MCP Inspector
473
+ fastmcp dev src/server.py
474
+ # Then open the inspector URL in browser
475
+ ```
476
+
477
+ ## Common Patterns
478
+
479
+ ### Async Tool
480
+
481
+ ```python
482
+ @mcp.tool()
483
+ async def fetch_data(url: str) -> dict:
484
+ """Fetch data from URL."""
485
+ import httpx
486
+ async with httpx.AsyncClient() as client:
487
+ response = await client.get(url)
488
+ return response.json()
489
+ ```
490
+
491
+ ### Tool with Validation
492
+
493
+ ```python
494
+ from pydantic import BaseModel, validator
495
+
496
+ class UserInput(BaseModel):
497
+ name: str
498
+ age: int
499
+
500
+ @validator("age")
501
+ def validate_age(cls, v):
502
+ if v < 0 or v > 150:
503
+ raise ValueError("Age must be between 0 and 150")
504
+ return v
505
+
506
+ @mcp.tool()
507
+ def create_user(user: UserInput) -> dict:
508
+ """Create a new user with validation."""
509
+ return {"id": generate_id(), **user.dict()}
510
+ ```
511
+
512
+ ### Context-aware Tool
513
+
514
+ ```python
515
+ @mcp.tool()
516
+ def get_context_info(ctx) -> dict:
517
+ """Get information about the current context."""
518
+ return {
519
+ "client_id": ctx.client_id if hasattr(ctx, "client_id") else "unknown",
520
+ "request_id": ctx.request_id if hasattr(ctx, "request_id") else "none",
521
+ }
522
+ ```