mcp-agent-kit 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- mcp_agent_kit-0.1.0/PKG-INFO +531 -0
- mcp_agent_kit-0.1.0/README.md +509 -0
- mcp_agent_kit-0.1.0/mcp_agent_kit/__init__.py +70 -0
- mcp_agent_kit-0.1.0/mcp_agent_kit/__main__.py +8 -0
- mcp_agent_kit-0.1.0/mcp_agent_kit/a2a.py +271 -0
- mcp_agent_kit-0.1.0/mcp_agent_kit/cli.py +480 -0
- mcp_agent_kit-0.1.0/mcp_agent_kit/client.py +365 -0
- mcp_agent_kit-0.1.0/mcp_agent_kit/errors.py +95 -0
- mcp_agent_kit-0.1.0/mcp_agent_kit/prompts.py +96 -0
- mcp_agent_kit-0.1.0/mcp_agent_kit/protocol.py +164 -0
- mcp_agent_kit-0.1.0/mcp_agent_kit/resources.py +68 -0
- mcp_agent_kit-0.1.0/mcp_agent_kit/server.py +364 -0
- mcp_agent_kit-0.1.0/mcp_agent_kit/skill.py +178 -0
- mcp_agent_kit-0.1.0/mcp_agent_kit/tools.py +250 -0
- mcp_agent_kit-0.1.0/mcp_agent_kit/validator.py +225 -0
- mcp_agent_kit-0.1.0/mcp_agent_kit.egg-info/PKG-INFO +531 -0
- mcp_agent_kit-0.1.0/mcp_agent_kit.egg-info/SOURCES.txt +28 -0
- mcp_agent_kit-0.1.0/mcp_agent_kit.egg-info/dependency_links.txt +1 -0
- mcp_agent_kit-0.1.0/mcp_agent_kit.egg-info/entry_points.txt +3 -0
- mcp_agent_kit-0.1.0/mcp_agent_kit.egg-info/requires.txt +6 -0
- mcp_agent_kit-0.1.0/mcp_agent_kit.egg-info/top_level.txt +2 -0
- mcp_agent_kit-0.1.0/pyproject.toml +45 -0
- mcp_agent_kit-0.1.0/setup.cfg +4 -0
- mcp_agent_kit-0.1.0/tests/test_a2a.py +120 -0
- mcp_agent_kit-0.1.0/tests/test_mcp_client.py +153 -0
- mcp_agent_kit-0.1.0/tests/test_mcp_server.py +240 -0
- mcp_agent_kit-0.1.0/tests/test_scaffolder.py +66 -0
- mcp_agent_kit-0.1.0/tests/test_skill.py +97 -0
- mcp_agent_kit-0.1.0/tools/__init__.py +6 -0
- mcp_agent_kit-0.1.0/tools/example.py +67 -0
|
@@ -0,0 +1,531 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: mcp-agent-kit
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Production-ready MCP server scaffold with stateless MCP 2026-07-28, A2A Agent Card, and Agent Skill packaging
|
|
5
|
+
Author-email: Abhishek Kaushik <abhishekk09827@gmail.com>
|
|
6
|
+
License: MIT
|
|
7
|
+
Keywords: mcp,model-context-protocol,agent,a2a,skill,scaffold
|
|
8
|
+
Classifier: Development Status :: 4 - Beta
|
|
9
|
+
Classifier: Intended Audience :: Developers
|
|
10
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
11
|
+
Classifier: Programming Language :: Python :: 3
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
15
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
16
|
+
Requires-Python: >=3.10
|
|
17
|
+
Description-Content-Type: text/markdown
|
|
18
|
+
Provides-Extra: a2a
|
|
19
|
+
Requires-Dist: flask>=3.0.0; extra == "a2a"
|
|
20
|
+
Provides-Extra: dev
|
|
21
|
+
Requires-Dist: pytest>=7.0.0; extra == "dev"
|
|
22
|
+
|
|
23
|
+
# mcp-agent-kit
|
|
24
|
+
|
|
25
|
+
`mcp-agent-kit` is an enterprise-grade scaffolding kit and runtime foundation that enables developers to build, validate, and ship a fully compliant, production-ready Model Context Protocol (MCP) server with Agent-to-Agent (A2A) protocol support and portable Agent Skill (`SKILL.md`) packaging in under 30 minutes. Built entirely with the Python standard library for zero runtime dependencies in the protocol core, it enforces strict stateless MCP specification compliance (version 2026-07-28), deterministic resource listing, schema design validation, dual-era negotiation, and automated tool-to-skill card mapping.
|
|
26
|
+
|
|
27
|
+
---
|
|
28
|
+
|
|
29
|
+
## Quick Start (5 Lines)
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
pip install mcp-agent-kit
|
|
33
|
+
create-mcp-agent my_agent
|
|
34
|
+
cd my_agent
|
|
35
|
+
python validate.py
|
|
36
|
+
python server.py
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
---
|
|
40
|
+
|
|
41
|
+
## End-to-End Demo Walkthrough (with Real Outputs)
|
|
42
|
+
|
|
43
|
+
Follow these step-by-step instructions to scaffold, validate, test, and run an MCP agent end-to-end.
|
|
44
|
+
|
|
45
|
+
### Step 1: Scaffold a New Agent
|
|
46
|
+
Run the generator CLI to create a new agent (e.g. `demo_agent`):
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
python -m mcp_agent_kit demo_agent --desc "Use when computing numerical metrics and data summaries. Do not use for unstructured text parsing."
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
**Terminal Output:**
|
|
53
|
+
```text
|
|
54
|
+
Scaffolding MCP agent 'demo_agent' into 'demo_agent'...
|
|
55
|
+
✓ Agent 'demo_agent' successfully created in 'demo_agent'.
|
|
56
|
+
Next steps:
|
|
57
|
+
cd demo_agent
|
|
58
|
+
python validate.py
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
---
|
|
62
|
+
|
|
63
|
+
### Step 2: Run Compliance Validation
|
|
64
|
+
Navigate into the agent directory and execute `validate.py`:
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
cd demo_agent
|
|
68
|
+
python validate.py
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
**Terminal Output:**
|
|
72
|
+
```text
|
|
73
|
+
✓ MCP server/discover responds correctly
|
|
74
|
+
✓ tools/list is deterministically ordered
|
|
75
|
+
✓ all tool schemas pass design rules
|
|
76
|
+
✓ A2A agent.json is valid
|
|
77
|
+
✓ SKILL.md portable core is valid
|
|
78
|
+
✓ dual-era client probe works against local server
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
---
|
|
82
|
+
|
|
83
|
+
### Step 3: Run the Dual-Era Probe Client Test
|
|
84
|
+
Verify client negotiation and tool execution via `client_test.py`:
|
|
85
|
+
|
|
86
|
+
```bash
|
|
87
|
+
python client_test.py
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
**Terminal Output:**
|
|
91
|
+
```text
|
|
92
|
+
..
|
|
93
|
+
----------------------------------------------------------------------
|
|
94
|
+
Ran 2 tests in 0.000s
|
|
95
|
+
|
|
96
|
+
OK
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
---
|
|
100
|
+
|
|
101
|
+
### Step 4: Test Stateless MCP 2026-07-28 Server via Stdio
|
|
102
|
+
Send raw JSON-RPC requests to `server.py` over stdin. Notice every request independently carries `_meta`:
|
|
103
|
+
|
|
104
|
+
#### Request A: Discovery (`server/discover`)
|
|
105
|
+
**Stdin Request:**
|
|
106
|
+
```json
|
|
107
|
+
{
|
|
108
|
+
"jsonrpc": "2.0",
|
|
109
|
+
"id": 1,
|
|
110
|
+
"method": "server/discover",
|
|
111
|
+
"params": {
|
|
112
|
+
"_meta": {
|
|
113
|
+
"protocolVersion": "2026-07-28",
|
|
114
|
+
"clientCapabilities": {},
|
|
115
|
+
"clientInfo": {"name": "demo-client", "version": "1.0.0"}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
**Stdout Response:**
|
|
122
|
+
```json
|
|
123
|
+
{
|
|
124
|
+
"jsonrpc": "2.0",
|
|
125
|
+
"id": 1,
|
|
126
|
+
"result": {
|
|
127
|
+
"resultType": "complete",
|
|
128
|
+
"serverInfo": {
|
|
129
|
+
"name": "demo_agent",
|
|
130
|
+
"version": "1.0.0"
|
|
131
|
+
},
|
|
132
|
+
"supportedVersions": [
|
|
133
|
+
"2026-07-28"
|
|
134
|
+
],
|
|
135
|
+
"capabilities": {
|
|
136
|
+
"tools": {"listChanged": false},
|
|
137
|
+
"resources": {"subscribe": false, "listChanged": false},
|
|
138
|
+
"prompts": {"listChanged": false}
|
|
139
|
+
},
|
|
140
|
+
"ttlMs": 3600000,
|
|
141
|
+
"cacheScope": "workspace"
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
#### Request B: Tool Execution (`tools/call`)
|
|
147
|
+
**Stdin Request:**
|
|
148
|
+
```json
|
|
149
|
+
{
|
|
150
|
+
"jsonrpc": "2.0",
|
|
151
|
+
"id": 2,
|
|
152
|
+
"method": "tools/call",
|
|
153
|
+
"params": {
|
|
154
|
+
"_meta": {
|
|
155
|
+
"protocolVersion": "2026-07-28",
|
|
156
|
+
"clientCapabilities": {},
|
|
157
|
+
"clientInfo": {"name": "demo-client", "version": "1.0.0"}
|
|
158
|
+
},
|
|
159
|
+
"name": "calculate_metrics",
|
|
160
|
+
"arguments": {
|
|
161
|
+
"metric_type": "mean",
|
|
162
|
+
"values": [12.5, 45.0, 78.5, 92.0]
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
**Stdout Response:**
|
|
169
|
+
```json
|
|
170
|
+
{
|
|
171
|
+
"jsonrpc": "2.0",
|
|
172
|
+
"id": 2,
|
|
173
|
+
"result": {
|
|
174
|
+
"resultType": "complete",
|
|
175
|
+
"serverInfo": {
|
|
176
|
+
"name": "demo_agent",
|
|
177
|
+
"version": "1.0.0"
|
|
178
|
+
},
|
|
179
|
+
"content": [
|
|
180
|
+
{
|
|
181
|
+
"type": "text",
|
|
182
|
+
"text": "{\"metric\": \"mean\", \"value\": 57.0, \"count\": 4}"
|
|
183
|
+
}
|
|
184
|
+
],
|
|
185
|
+
"isError": false
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
---
|
|
191
|
+
|
|
192
|
+
### Step 5: Test A2A Agent Card & Task Lifecycle API
|
|
193
|
+
Start the HTTP server on port 8000:
|
|
194
|
+
|
|
195
|
+
```bash
|
|
196
|
+
python a2a/server.py 8000
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
#### A. Fetch the Agent Card
|
|
200
|
+
```bash
|
|
201
|
+
curl -s http://localhost:8000/.well-known/agent.json
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
**HTTP 200 Response:**
|
|
205
|
+
```json
|
|
206
|
+
{
|
|
207
|
+
"schemaVersion": "1.0",
|
|
208
|
+
"name": "demo_agent",
|
|
209
|
+
"description": "Use when computing numerical metrics and data summaries. Do not use for unstructured text parsing.",
|
|
210
|
+
"url": "http://localhost:8000/a2a",
|
|
211
|
+
"version": "1.0.0",
|
|
212
|
+
"skills": [
|
|
213
|
+
{
|
|
214
|
+
"id": "calculate_metrics",
|
|
215
|
+
"name": "calculate_metrics",
|
|
216
|
+
"description": "Use when analyzing numerical series or computing aggregate metrics. Do not use for unstructured text parsing.",
|
|
217
|
+
"inputModes": ["data", "text"],
|
|
218
|
+
"outputModes": ["data", "text"],
|
|
219
|
+
"parameters": {
|
|
220
|
+
"type": "object",
|
|
221
|
+
"properties": {
|
|
222
|
+
"metric_type": {"type": "string", "enum": ["mean", "sum", "max", "min"]},
|
|
223
|
+
"values": {"type": "array", "items": {"type": "number"}}
|
|
224
|
+
},
|
|
225
|
+
"required": ["values"]
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
],
|
|
229
|
+
"capabilities": {
|
|
230
|
+
"streaming": true,
|
|
231
|
+
"pushNotifications": false
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
#### B. Send a Task (`POST /a2a/tasks/send`)
|
|
237
|
+
```bash
|
|
238
|
+
curl -s -X POST http://localhost:8000/a2a/tasks/send \
|
|
239
|
+
-H "Content-Type: application/json" \
|
|
240
|
+
-d '{
|
|
241
|
+
"messages": [
|
|
242
|
+
{
|
|
243
|
+
"role": "user",
|
|
244
|
+
"parts": [
|
|
245
|
+
{"type": "text", "text": "Compute quarterly summary metrics"},
|
|
246
|
+
{"type": "data", "data": {"quarter": "Q3", "values": [100, 200, 300]}}
|
|
247
|
+
]
|
|
248
|
+
}
|
|
249
|
+
]
|
|
250
|
+
}'
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
**HTTP 200 Response:**
|
|
254
|
+
```json
|
|
255
|
+
{
|
|
256
|
+
"taskId": "task_e8ca2985820b",
|
|
257
|
+
"status": "completed"
|
|
258
|
+
}
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
#### C. Query Task Status and Output Artifact (`GET /a2a/tasks/{id}`)
|
|
262
|
+
```bash
|
|
263
|
+
curl -s http://localhost:8000/a2a/tasks/task_e8ca2985820b
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
**HTTP 200 Response:**
|
|
267
|
+
```json
|
|
268
|
+
{
|
|
269
|
+
"taskId": "task_e8ca2985820b",
|
|
270
|
+
"status": "completed",
|
|
271
|
+
"messages": [
|
|
272
|
+
{
|
|
273
|
+
"role": "user",
|
|
274
|
+
"parts": [
|
|
275
|
+
{"type": "text", "text": "Compute quarterly summary metrics"},
|
|
276
|
+
{"type": "data", "data": {"quarter": "Q3", "values": [100, 200, 300]}}
|
|
277
|
+
]
|
|
278
|
+
}
|
|
279
|
+
],
|
|
280
|
+
"artifacts": [
|
|
281
|
+
{
|
|
282
|
+
"name": "task_output",
|
|
283
|
+
"type": "data",
|
|
284
|
+
"data": {
|
|
285
|
+
"status": "success",
|
|
286
|
+
"processed_parts": 1
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
],
|
|
290
|
+
"created_at": 1791728650.129,
|
|
291
|
+
"updated_at": 1791728650.130
|
|
292
|
+
}
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
---
|
|
296
|
+
|
|
297
|
+
### Step 6: Run Comprehensive Test Suite
|
|
298
|
+
Run the 20-test automated compliance and unit testing suite:
|
|
299
|
+
|
|
300
|
+
```bash
|
|
301
|
+
python -m unittest discover -s tests -p "test_*.py" -v
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
**Terminal Output:**
|
|
305
|
+
```text
|
|
306
|
+
test_artifact_returned_on_completion (test_a2a.TestA2A.test_artifact_returned_on_completion) ... ok
|
|
307
|
+
test_input_required_pause_and_resume (test_a2a.TestA2A.test_input_required_pause_and_resume) ... ok
|
|
308
|
+
test_mcp_tools_to_a2a_skills_mapping (test_a2a.TestA2A.test_mcp_tools_to_a2a_skills_mapping) ... ok
|
|
309
|
+
test_task_lifecycle_submitted_to_working_to_completed (test_a2a.TestA2A.test_task_lifecycle_submitted_to_working_to_completed) ... ok
|
|
310
|
+
test_allowlisted_peer_timeout_stays_unavailable (test_mcp_client.TestMCPClient.test_allowlisted_peer_timeout_stays_unavailable) ... ok
|
|
311
|
+
test_allowlisted_peer_valid_initialize_selects_legacy (test_mcp_client.TestMCPClient.test_allowlisted_peer_valid_initialize_selects_legacy) ... ok
|
|
312
|
+
test_ambiguous_signal_fails_closed_without_allowlist (test_mcp_client.TestMCPClient.test_ambiguous_signal_fails_closed_without_allowlist) ... ok
|
|
313
|
+
test_collision_produces_prefixed_name_never_silent_overwrite (test_mcp_client.TestMCPClient.test_collision_produces_prefixed_name_never_silent_overwrite) ... ok
|
|
314
|
+
test_recognized_modern_errors_never_trigger_initialize (test_mcp_client.TestMCPClient.test_recognized_modern_errors_never_trigger_initialize) ... ok
|
|
315
|
+
test_every_request_repeats_meta_independently (test_mcp_server.TestMCPServer.test_every_request_repeats_meta_independently) ... ok
|
|
316
|
+
test_list_results_stable_regardless_of_insertion_order (test_mcp_server.TestMCPServer.test_list_results_stable_regardless_of_insertion_order) ... ok
|
|
317
|
+
test_server_never_initiates_request (test_mcp_server.TestMCPServer.test_server_never_initiates_request) ... ok
|
|
318
|
+
test_stdio_loop (test_mcp_server.TestMCPServer.test_stdio_loop) ... ok
|
|
319
|
+
test_tool_call_returns_isError_true_on_executor_failure (test_mcp_server.TestMCPServer.test_tool_call_returns_isError_true_on_executor_failure) ... ok
|
|
320
|
+
test_unsupported_protocol_version_error (test_mcp_server.TestMCPServer.test_unsupported_protocol_version_error) ... ok
|
|
321
|
+
test_scaffold_creates_complete_working_structure (test_scaffolder.TestScaffolder.test_scaffold_creates_complete_working_structure) ... ok
|
|
322
|
+
test_description_length_and_pattern_checks (test_skill.TestSkillPackage.test_description_length_and_pattern_checks) ... ok
|
|
323
|
+
test_generate_skill_markdown_creates_compliant_output (test_skill.TestSkillPackage.test_generate_skill_markdown_creates_compliant_output) ... ok
|
|
324
|
+
test_skill_name_matches_directory (test_skill.TestSkillPackage.test_skill_name_matches_directory) ... ok
|
|
325
|
+
test_unknown_runtime_extensions_rejected_in_portable_core (test_skill.TestSkillPackage.test_unknown_runtime_extensions_rejected_in_portable_core) ... ok
|
|
326
|
+
|
|
327
|
+
----------------------------------------------------------------------
|
|
328
|
+
Ran 20 tests in 0.033s
|
|
329
|
+
|
|
330
|
+
OK
|
|
331
|
+
```
|
|
332
|
+
|
|
333
|
+
---
|
|
334
|
+
|
|
335
|
+
## Architecture
|
|
336
|
+
|
|
337
|
+
```
|
|
338
|
+
+=============================================================================+
|
|
339
|
+
| Host Environment / AI Assistant |
|
|
340
|
+
| (Claude Desktop, Claude Code, Anthropic API, Custom Host) |
|
|
341
|
+
+=============================================================================+
|
|
342
|
+
| | |
|
|
343
|
+
| [Stateless JSON-RPC] | [REST / Task Lifecycle] | [Progressive Disclosure]
|
|
344
|
+
| (stdio transport) | (HTTP /a2a) | (Agent Skill)
|
|
345
|
+
v v v
|
|
346
|
+
+-----------------------+ +-----------------------+ +-----------------+
|
|
347
|
+
| MCP Server Core | | A2A Task Server | | SKILL.md |
|
|
348
|
+
| (Stateless 2026) | | (v1.0) | | (Portable Core) |
|
|
349
|
+
+-----------------------+ +-----------------------+ +-----------------+
|
|
350
|
+
| - server/discover | | - /.well-known/agent | | - Frontmatter |
|
|
351
|
+
| - tools/list & call |<----->| - tasks/send (Parts) | | - Workflows |
|
|
352
|
+
| - resources/list&read | Auto- | - tasks/{id} | | - Tool index |
|
|
353
|
+
| - prompts/list & get | Map | - Artifacts pipeline | | |
|
|
354
|
+
| - input_required mode | | - input_required pause| | |
|
|
355
|
+
+-----------------------+ +-----------------------+ +-----------------+
|
|
356
|
+
|
|
|
357
|
+
+-----------------------------------+
|
|
358
|
+
v
|
|
359
|
+
+---------------------------------------+
|
|
360
|
+
| Compliant Tool Schemas |
|
|
361
|
+
| - snake_case names |
|
|
362
|
+
| - "Use when X. Do not use for Y." |
|
|
363
|
+
| - readOnlyHint / destructiveHint |
|
|
364
|
+
| - Deterministic alphabetical order |
|
|
365
|
+
+---------------------------------------+
|
|
366
|
+
```
|
|
367
|
+
|
|
368
|
+
---
|
|
369
|
+
|
|
370
|
+
## How to Add a New Tool (3 Steps)
|
|
371
|
+
|
|
372
|
+
### Step 1: Define the function & schema in `tools/`
|
|
373
|
+
Create a new file in `tools/` (e.g., `tools/user_ops.py`):
|
|
374
|
+
|
|
375
|
+
```python
|
|
376
|
+
from typing import Any, Dict
|
|
377
|
+
|
|
378
|
+
def get_user_profile(arguments: Dict[str, Any]) -> Dict[str, Any]:
|
|
379
|
+
user_id = arguments["user_id"]
|
|
380
|
+
return {"user_id": user_id, "status": "active", "tier": "enterprise"}
|
|
381
|
+
|
|
382
|
+
TOOL_DEFINITION = {
|
|
383
|
+
"name": "get_user_profile",
|
|
384
|
+
"description": "Use when retrieving user profile details by ID. Do not use for updating account settings.",
|
|
385
|
+
"inputSchema": {
|
|
386
|
+
"type": "object",
|
|
387
|
+
"properties": {
|
|
388
|
+
"user_id": {"type": "string", "description": "Unique user ID"}
|
|
389
|
+
},
|
|
390
|
+
"required": ["user_id"]
|
|
391
|
+
},
|
|
392
|
+
"annotations": {
|
|
393
|
+
"readOnlyHint": True,
|
|
394
|
+
"destructiveHint": False,
|
|
395
|
+
"idempotentHint": True,
|
|
396
|
+
"openWorldHint": False
|
|
397
|
+
},
|
|
398
|
+
"handler": get_user_profile
|
|
399
|
+
}
|
|
400
|
+
```
|
|
401
|
+
|
|
402
|
+
### Step 2: Register the tool in `server.py`
|
|
403
|
+
In `server.py`, register the tool definition with the server engine:
|
|
404
|
+
|
|
405
|
+
```python
|
|
406
|
+
from tools.user_ops import TOOL_DEFINITION as USER_TOOL
|
|
407
|
+
|
|
408
|
+
srv.tools.register(
|
|
409
|
+
name=USER_TOOL["name"],
|
|
410
|
+
description=USER_TOOL["description"],
|
|
411
|
+
input_schema=USER_TOOL["inputSchema"],
|
|
412
|
+
annotations=USER_TOOL["annotations"],
|
|
413
|
+
handler=USER_TOOL["handler"]
|
|
414
|
+
)
|
|
415
|
+
```
|
|
416
|
+
|
|
417
|
+
### Step 3: Verify with `validate.py`
|
|
418
|
+
```bash
|
|
419
|
+
python validate.py
|
|
420
|
+
```
|
|
421
|
+
|
|
422
|
+
---
|
|
423
|
+
|
|
424
|
+
## How to Install in Claude Desktop
|
|
425
|
+
|
|
426
|
+
Add your server configuration to your `claude_desktop_config.json`:
|
|
427
|
+
|
|
428
|
+
- **macOS**: `~/Library/Application Support/Claude/claude_desktop_config.json`
|
|
429
|
+
- **Windows**: `%APPDATA%\Claude\claude_desktop_config.json`
|
|
430
|
+
- **Linux**: `~/.config/Claude/claude_desktop_config.json`
|
|
431
|
+
|
|
432
|
+
```json
|
|
433
|
+
{
|
|
434
|
+
"mcpServers": {
|
|
435
|
+
"my_agent": {
|
|
436
|
+
"command": "python",
|
|
437
|
+
"args": ["/absolute/path/to/my_agent/server.py"]
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
```
|
|
442
|
+
|
|
443
|
+
---
|
|
444
|
+
|
|
445
|
+
## How to Install as Agent Skill in Claude Code
|
|
446
|
+
|
|
447
|
+
Run this single command to install as a project-level or global Agent Skill:
|
|
448
|
+
|
|
449
|
+
```bash
|
|
450
|
+
mkdir -p ~/.claude/skills && cp SKILL.md ~/.claude/skills/my_agent.md
|
|
451
|
+
```
|
|
452
|
+
|
|
453
|
+
Or for project-level scope in your repository:
|
|
454
|
+
|
|
455
|
+
```bash
|
|
456
|
+
mkdir -p .claude/skills && cp SKILL.md .claude/skills/my_agent.md
|
|
457
|
+
```
|
|
458
|
+
|
|
459
|
+
---
|
|
460
|
+
|
|
461
|
+
## Validation Engine (`validate.py`)
|
|
462
|
+
|
|
463
|
+
Run the complete compliance suite with a single command:
|
|
464
|
+
|
|
465
|
+
```bash
|
|
466
|
+
python validate.py
|
|
467
|
+
```
|
|
468
|
+
|
|
469
|
+
### Example Output
|
|
470
|
+
|
|
471
|
+
```text
|
|
472
|
+
✓ MCP server/discover responds correctly
|
|
473
|
+
✓ tools/list is deterministically ordered
|
|
474
|
+
✓ all tool schemas pass design rules
|
|
475
|
+
✓ A2A agent.json is valid
|
|
476
|
+
✓ SKILL.md portable core is valid
|
|
477
|
+
✓ dual-era client probe works against local server
|
|
478
|
+
```
|
|
479
|
+
|
|
480
|
+
If a tool violates schema rules (e.g. missing annotations or improper description formatting), the validator detects and reports it with granular diagnostics:
|
|
481
|
+
|
|
482
|
+
```text
|
|
483
|
+
✗ notes_delete missing readOnlyHint or destructiveHint (at least one must be true)
|
|
484
|
+
```
|
|
485
|
+
|
|
486
|
+
---
|
|
487
|
+
|
|
488
|
+
## What `mcp-agent-kit` Implements
|
|
489
|
+
|
|
490
|
+
### 1. Stateless MCP Specification (2026-07-28)
|
|
491
|
+
- **Zero Stateful Coupling**: Every JSON-RPC request must carry a valid `_meta` block (`protocolVersion`, `clientCapabilities`, `clientInfo`) and is validated independently without session reuse.
|
|
492
|
+
- **Envelope Contracts**: Every successful response includes `resultType: "complete"` and server identity (`serverInfo`).
|
|
493
|
+
- **Input Required Protocol**: Handlers needing interaction yield `resultType: "input_required"`. The server never initiates unsolicited requests to the client.
|
|
494
|
+
- **Deterministic Alphabetical Sorting**: All collections (`tools/list`, `resources/list`, `prompts/list`) are deterministically sorted.
|
|
495
|
+
- **Stdio Rules**: JSON-RPC is strictly emitted to `stdout` with newline flushing; diagnostics and logs are written exclusively to `stderr`.
|
|
496
|
+
|
|
497
|
+
### 2. Dual-Era Probe Client & Namespace Merger
|
|
498
|
+
- **Probing State Machine**: Starts with modern `server/discover`.
|
|
499
|
+
- **Fail-Closed Security**: Recognized modern errors (`-32020`, `-32021`, `-32022`) correct and retry without falling back to initialize. Ambiguous signals fail closed unless explicitly allowlisted.
|
|
500
|
+
- **Multi-Server Namespace Merging**: Colliding tool names are prefixed (`server/tool_name`), avoiding silent overwrites while preserving local names.
|
|
501
|
+
|
|
502
|
+
### 3. A2A Protocol (Agent-to-Agent v1.0)
|
|
503
|
+
- Auto-generates `/.well-known/agent.json` from registered tools.
|
|
504
|
+
- Task lifecycle management: `submitted` → `working` → `completed` | `failed` | `input_required`.
|
|
505
|
+
- Rich message parts (`text`, `file`, `data`) and typed output artifacts upon completion.
|
|
506
|
+
|
|
507
|
+
### 4. Portable Agent Skill (`SKILL.md`)
|
|
508
|
+
- YAML frontmatter with strict portable core validation (`name` matches directory, `description` 20–1024 characters with `"Use when X. Do not use for Y."` pattern).
|
|
509
|
+
- Zero unknown runtime extensions.
|
|
510
|
+
|
|
511
|
+
---
|
|
512
|
+
|
|
513
|
+
## Architecture Boundaries (What NOT to Build)
|
|
514
|
+
|
|
515
|
+
- **Authentication**: Zero hardcoded auth in core protocol. To add auth, attach a bearer token validator in `a2a/server.py` or middleware headers in `MCPServer._dispatch`.
|
|
516
|
+
- **Database**: Zero database dependencies. In-memory task and state stores are provided with cleanly documented swap points (`TaskManager.task_executor`).
|
|
517
|
+
- **Dependencies**: Protocol core uses Python standard library only (`json`, `dataclasses`, `http.server`, `re`, `sys`). Flask is optional: `pip install mcp-agent-kit[a2a]`.
|
|
518
|
+
|
|
519
|
+
---
|
|
520
|
+
|
|
521
|
+
## Specification References
|
|
522
|
+
|
|
523
|
+
- [Model Context Protocol (MCP) Specification 2026-07-28](https://spec.modelcontextprotocol.io)
|
|
524
|
+
- [Agent-to-Agent (A2A) Protocol Specification v1.0](https://a2a-protocol.org)
|
|
525
|
+
- [Portable Agent Skill Specification](https://docs.anthropic.com/en/docs/agents-and-tools)
|
|
526
|
+
|
|
527
|
+
---
|
|
528
|
+
|
|
529
|
+
## License
|
|
530
|
+
|
|
531
|
+
MIT © [Abhishek Kaushik](https://github.com/abhishekk09827)
|