mcp-agent-kit
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.
Quick Start (5 Lines)
pip install mcp-agent-kit
create-mcp-agent my_agent
cd my_agent
python validate.py
python server.py
End-to-End Demo Walkthrough (with Real Outputs)
Follow these step-by-step instructions to scaffold, validate, test, and run an MCP agent end-to-end.
Step 1: Scaffold a New Agent
Run the generator CLI to create a new agent (e.g. demo_agent):
python -m mcp_agent_kit demo_agent --desc "Use when computing numerical metrics and data summaries. Do not use for unstructured text parsing."
Terminal Output:
Scaffolding MCP agent 'demo_agent' into 'demo_agent'...
✓ Agent 'demo_agent' successfully created in 'demo_agent'.
Next steps:
cd demo_agent
python validate.py
Step 2: Run Compliance Validation
Navigate into the agent directory and execute validate.py:
cd demo_agent
python validate.py
Terminal Output:
✓ MCP server/discover responds correctly
✓ tools/list is deterministically ordered
✓ all tool schemas pass design rules
✓ A2A agent.json is valid
✓ SKILL.md portable core is valid
✓ dual-era client probe works against local server
Step 3: Run the Dual-Era Probe Client Test
Verify client negotiation and tool execution via client_test.py:
python client_test.py
Terminal Output:
..
----------------------------------------------------------------------
Ran 2 tests in 0.000s
OK
Step 4: Test Stateless MCP 2026-07-28 Server via Stdio
Send raw JSON-RPC requests to server.py over stdin. Notice every request independently carries _meta:
Request A: Discovery (server/discover)
Stdin Request:
{
"jsonrpc": "2.0",
"id": 1,
"method": "server/discover",
"params": {
"_meta": {
"protocolVersion": "2026-07-28",
"clientCapabilities": {},
"clientInfo": {"name": "demo-client", "version": "1.0.0"}
}
}
}
Stdout Response:
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"resultType": "complete",
"serverInfo": {
"name": "demo_agent",
"version": "1.0.0"
},
"supportedVersions": [
"2026-07-28"
],
"capabilities": {
"tools": {"listChanged": false},
"resources": {"subscribe": false, "listChanged": false},
"prompts": {"listChanged": false}
},
"ttlMs": 3600000,
"cacheScope": "workspace"
}
}
Request B: Tool Execution (tools/call)
Stdin Request:
{
"jsonrpc": "2.0",
"id": 2,
"method": "tools/call",
"params": {
"_meta": {
"protocolVersion": "2026-07-28",
"clientCapabilities": {},
"clientInfo": {"name": "demo-client", "version": "1.0.0"}
},
"name": "calculate_metrics",
"arguments": {
"metric_type": "mean",
"values": [12.5, 45.0, 78.5, 92.0]
}
}
}
Stdout Response:
{
"jsonrpc": "2.0",
"id": 2,
"result": {
"resultType": "complete",
"serverInfo": {
"name": "demo_agent",
"version": "1.0.0"
},
"content": [
{
"type": "text",
"text": "{\"metric\": \"mean\", \"value\": 57.0, \"count\": 4}"
}
],
"isError": false
}
}
Step 5: Test A2A Agent Card & Task Lifecycle API
Start the HTTP server on port 8000:
python a2a/server.py 8000
A. Fetch the Agent Card
curl -s http://localhost:8000/.well-known/agent.json
HTTP 200 Response:
{
"schemaVersion": "1.0",
"name": "demo_agent",
"description": "Use when computing numerical metrics and data summaries. Do not use for unstructured text parsing.",
"url": "http://localhost:8000/a2a",
"version": "1.0.0",
"skills": [
{
"id": "calculate_metrics",
"name": "calculate_metrics",
"description": "Use when analyzing numerical series or computing aggregate metrics. Do not use for unstructured text parsing.",
"inputModes": ["data", "text"],
"outputModes": ["data", "text"],
"parameters": {
"type": "object",
"properties": {
"metric_type": {"type": "string", "enum": ["mean", "sum", "max", "min"]},
"values": {"type": "array", "items": {"type": "number"}}
},
"required": ["values"]
}
}
],
"capabilities": {
"streaming": true,
"pushNotifications": false
}
}
B. Send a Task (POST /a2a/tasks/send)
curl -s -X POST http://localhost:8000/a2a/tasks/send \
-H "Content-Type: application/json" \
-d '{
"messages": [
{
"role": "user",
"parts": [
{"type": "text", "text": "Compute quarterly summary metrics"},
{"type": "data", "data": {"quarter": "Q3", "values": [100, 200, 300]}}
]
}
]
}'
HTTP 200 Response:
{
"taskId": "task_e8ca2985820b",
"status": "completed"
}
C. Query Task Status and Output Artifact (GET /a2a/tasks/{id})
curl -s http://localhost:8000/a2a/tasks/task_e8ca2985820b
HTTP 200 Response:
{
"taskId": "task_e8ca2985820b",
"status": "completed",
"messages": [
{
"role": "user",
"parts": [
{"type": "text", "text": "Compute quarterly summary metrics"},
{"type": "data", "data": {"quarter": "Q3", "values": [100, 200, 300]}}
]
}
],
"artifacts": [
{
"name": "task_output",
"type": "data",
"data": {
"status": "success",
"processed_parts": 1
}
}
],
"created_at": 1791728650.129,
"updated_at": 1791728650.130
}
Step 6: Run Comprehensive Test Suite
Run the 20-test automated compliance and unit testing suite:
python -m unittest discover -s tests -p "test_*.py" -v
Terminal Output:
test_artifact_returned_on_completion (test_a2a.TestA2A.test_artifact_returned_on_completion) ... ok
test_input_required_pause_and_resume (test_a2a.TestA2A.test_input_required_pause_and_resume) ... ok
test_mcp_tools_to_a2a_skills_mapping (test_a2a.TestA2A.test_mcp_tools_to_a2a_skills_mapping) ... ok
test_task_lifecycle_submitted_to_working_to_completed (test_a2a.TestA2A.test_task_lifecycle_submitted_to_working_to_completed) ... ok
test_allowlisted_peer_timeout_stays_unavailable (test_mcp_client.TestMCPClient.test_allowlisted_peer_timeout_stays_unavailable) ... ok
test_allowlisted_peer_valid_initialize_selects_legacy (test_mcp_client.TestMCPClient.test_allowlisted_peer_valid_initialize_selects_legacy) ... ok
test_ambiguous_signal_fails_closed_without_allowlist (test_mcp_client.TestMCPClient.test_ambiguous_signal_fails_closed_without_allowlist) ... ok
test_collision_produces_prefixed_name_never_silent_overwrite (test_mcp_client.TestMCPClient.test_collision_produces_prefixed_name_never_silent_overwrite) ... ok
test_recognized_modern_errors_never_trigger_initialize (test_mcp_client.TestMCPClient.test_recognized_modern_errors_never_trigger_initialize) ... ok
test_every_request_repeats_meta_independently (test_mcp_server.TestMCPServer.test_every_request_repeats_meta_independently) ... ok
test_list_results_stable_regardless_of_insertion_order (test_mcp_server.TestMCPServer.test_list_results_stable_regardless_of_insertion_order) ... ok
test_server_never_initiates_request (test_mcp_server.TestMCPServer.test_server_never_initiates_request) ... ok
test_stdio_loop (test_mcp_server.TestMCPServer.test_stdio_loop) ... ok
test_tool_call_returns_isError_true_on_executor_failure (test_mcp_server.TestMCPServer.test_tool_call_returns_isError_true_on_executor_failure) ... ok
test_unsupported_protocol_version_error (test_mcp_server.TestMCPServer.test_unsupported_protocol_version_error) ... ok
test_scaffold_creates_complete_working_structure (test_scaffolder.TestScaffolder.test_scaffold_creates_complete_working_structure) ... ok
test_description_length_and_pattern_checks (test_skill.TestSkillPackage.test_description_length_and_pattern_checks) ... ok
test_generate_skill_markdown_creates_compliant_output (test_skill.TestSkillPackage.test_generate_skill_markdown_creates_compliant_output) ... ok
test_skill_name_matches_directory (test_skill.TestSkillPackage.test_skill_name_matches_directory) ... ok
test_unknown_runtime_extensions_rejected_in_portable_core (test_skill.TestSkillPackage.test_unknown_runtime_extensions_rejected_in_portable_core) ... ok
----------------------------------------------------------------------
Ran 20 tests in 0.033s
OK
Architecture
+=============================================================================+
| Host Environment / AI Assistant |
| (Claude Desktop, Claude Code, Anthropic API, Custom Host) |
+=============================================================================+
| | |
| [Stateless JSON-RPC] | [REST / Task Lifecycle] | [Progressive Disclosure]
| (stdio transport) | (HTTP /a2a) | (Agent Skill)
v v v
+-----------------------+ +-----------------------+ +-----------------+
| MCP Server Core | | A2A Task Server | | SKILL.md |
| (Stateless 2026) | | (v1.0) | | (Portable Core) |
+-----------------------+ +-----------------------+ +-----------------+
| - server/discover | | - /.well-known/agent | | - Frontmatter |
| - tools/list & call |<----->| - tasks/send (Parts) | | - Workflows |
| - resources/list&read | Auto- | - tasks/{id} | | - Tool index |
| - prompts/list & get | Map | - Artifacts pipeline | | |
| - input_required mode | | - input_required pause| | |
+-----------------------+ +-----------------------+ +-----------------+
|
+-----------------------------------+
v
+---------------------------------------+
| Compliant Tool Schemas |
| - snake_case names |
| - "Use when X. Do not use for Y." |
| - readOnlyHint / destructiveHint |
| - Deterministic alphabetical order |
+---------------------------------------+
How to Add a New Tool (3 Steps)
Step 1: Define the function & schema in tools/
Create a new file in tools/ (e.g., tools/user_ops.py):
from typing import Any, Dict
def get_user_profile(arguments: Dict[str, Any]) -> Dict[str, Any]:
user_id = arguments["user_id"]
return {"user_id": user_id, "status": "active", "tier": "enterprise"}
TOOL_DEFINITION = {
"name": "get_user_profile",
"description": "Use when retrieving user profile details by ID. Do not use for updating account settings.",
"inputSchema": {
"type": "object",
"properties": {
"user_id": {"type": "string", "description": "Unique user ID"}
},
"required": ["user_id"]
},
"annotations": {
"readOnlyHint": True,
"destructiveHint": False,
"idempotentHint": True,
"openWorldHint": False
},
"handler": get_user_profile
}
Step 2: Register the tool in server.py
In server.py, register the tool definition with the server engine:
from tools.user_ops import TOOL_DEFINITION as USER_TOOL
srv.tools.register(
name=USER_TOOL["name"],
description=USER_TOOL["description"],
input_schema=USER_TOOL["inputSchema"],
annotations=USER_TOOL["annotations"],
handler=USER_TOOL["handler"]
)
Step 3: Verify with validate.py
python validate.py
How to Install in Claude Desktop
Add your server configuration to your claude_desktop_config.json:
- macOS:
~/Library/Application Support/Claude/claude_desktop_config.json - Windows:
%APPDATA%\Claude\claude_desktop_config.json - Linux:
~/.config/Claude/claude_desktop_config.json
{
"mcpServers": {
"my_agent": {
"command": "python",
"args": ["/absolute/path/to/my_agent/server.py"]
}
}
}
How to Install as Agent Skill in Claude Code
Run this single command to install as a project-level or global Agent Skill:
mkdir -p ~/.claude/skills && cp SKILL.md ~/.claude/skills/my_agent.md
Or for project-level scope in your repository:
mkdir -p .claude/skills && cp SKILL.md .claude/skills/my_agent.md
Validation Engine (validate.py)
Run the complete compliance suite with a single command:
python validate.py
Example Output
✓ MCP server/discover responds correctly
✓ tools/list is deterministically ordered
✓ all tool schemas pass design rules
✓ A2A agent.json is valid
✓ SKILL.md portable core is valid
✓ dual-era client probe works against local server
If a tool violates schema rules (e.g. missing annotations or improper description formatting), the validator detects and reports it with granular diagnostics:
✗ notes_delete missing readOnlyHint or destructiveHint (at least one must be true)
What mcp-agent-kit Implements
1. Stateless MCP Specification (2026-07-28)
- Zero Stateful Coupling: Every JSON-RPC request must carry a valid
_metablock (protocolVersion,clientCapabilities,clientInfo) and is validated independently without session reuse. - Envelope Contracts: Every successful response includes
resultType: "complete"and server identity (serverInfo). - Input Required Protocol: Handlers needing interaction yield
resultType: "input_required". The server never initiates unsolicited requests to the client. - Deterministic Alphabetical Sorting: All collections (
tools/list,resources/list,prompts/list) are deterministically sorted. - Stdio Rules: JSON-RPC is strictly emitted to
stdoutwith newline flushing; diagnostics and logs are written exclusively tostderr.
2. Dual-Era Probe Client & Namespace Merger
- Probing State Machine: Starts with modern
server/discover. - Fail-Closed Security: Recognized modern errors (
-32020,-32021,-32022) correct and retry without falling back to initialize. Ambiguous signals fail closed unless explicitly allowlisted. - Multi-Server Namespace Merging: Colliding tool names are prefixed (
server/tool_name), avoiding silent overwrites while preserving local names.
3. A2A Protocol (Agent-to-Agent v1.0)
- Auto-generates
/.well-known/agent.jsonfrom registered tools. - Task lifecycle management:
submitted→working→completed|failed|input_required. - Rich message parts (
text,file,data) and typed output artifacts upon completion.
4. Portable Agent Skill (SKILL.md)
- YAML frontmatter with strict portable core validation (
namematches directory,description20–1024 characters with"Use when X. Do not use for Y."pattern). - Zero unknown runtime extensions.
Architecture Boundaries (What NOT to Build)
- Authentication: Zero hardcoded auth in core protocol. To add auth, attach a bearer token validator in
a2a/server.pyor middleware headers inMCPServer._dispatch. - Database: Zero database dependencies. In-memory task and state stores are provided with cleanly documented swap points (
TaskManager.task_executor). - Dependencies: Protocol core uses Python standard library only (
json,dataclasses,http.server,re,sys). Flask is optional:pip install mcp-agent-kit[a2a].
Specification References
- Model Context Protocol (MCP) Specification 2026-07-28
- Agent-to-Agent (A2A) Protocol Specification v1.0
- Portable Agent Skill Specification
License
MIT © Abhishek Kaushik
Metadata
Release files for mcp-agent-kit 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| mcp_agent_kit-0.1.0.tar.gz | 40.3 kB | Without provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| mcp_agent_kit-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 76.1 kB
Release files / mcp_agent_kit-0.1.0.tar.gz
| Download URL | mcp_agent_kit-0.1.0.tar.gz |
|---|---|
| Size | 40.3 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
a7dd84d0d1f9002de4f2ed700e62370266adcd49b1a40f8f6467c090634b3827
|
|
BLAKE2b-256 checksum How to use checksums |
f0587ecaa4ca10735abad50ed7347a217388d3b43bcfd35c6510b5310f3def8c
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.12.3
|
Release files / mcp_agent_kit-0.1.0-py3-none-any.whl
| Download URL | mcp_agent_kit-0.1.0-py3-none-any.whl |
|---|---|
| Size | 35.8 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
e56e498be1fcf5985023bfdc8698ff7ec7a5941805732e3e43ef20741adccacd
|
|
BLAKE2b-256 checksum How to use checksums |
fe9e4af0da4a878a0401a160f9746ee19ec5ec55c91121bd81e95a63f4580b48
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.12.3
|
Security for mcp-agent-kit 0.1.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.