Skip to content

Commit 678e5d4

Browse files
Andy Caiclaude
andcommitted
Add agentcard-mcp: MCP server for AgentCard v1.0 identity layer
Adds adapters/python/mcp-server/ — a lightweight Python MCP server that gives any Claude/LLM agent an AgentCard v1.0 identity. Tools: agentcard_declare — register identity (session registry) agentcard_resolve — look up peers by name or ULID agentcard_validate — validate JSON against AgentCard schema agentcard_list — list all registered agents Resources: agentcard://schema — embedded AgentCard v1.0 JSON Schema agentcard://registry — all declared cards as JSON array Also includes server.json for registry.modelcontextprotocol.io submission and 36 unit tests (zero external dependencies in test mode). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 37ec16a commit 678e5d4

8 files changed

Lines changed: 1097 additions & 0 deletions

File tree

Lines changed: 178 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,178 @@
1+
# agentcard-mcp
2+
3+
> **AgentCard v1.0 identity layer for agent-to-agent (A2A) communication.**
4+
> Give any Claude / LLM agent a machine-readable identity using the open [AgentCard](https://github.com/kwailapt/AgentCard) standard.
5+
6+
[![Apache 2.0](https://img.shields.io/badge/license-Apache%202.0-blue.svg)](../../LICENSE)
7+
[![MCP Compatible](https://img.shields.io/badge/MCP-compatible-green.svg)](https://modelcontextprotocol.io)
8+
[![AgentCard v1.0](https://img.shields.io/badge/AgentCard-v1.0-orange.svg)](https://github.com/kwailapt/AgentCard)
9+
10+
## What is AgentCard?
11+
12+
AgentCard is to A2A communication what HTTP headers are to the web:
13+
a standardised, machine-parseable capability declaration that works
14+
with any framework (LangChain, CrewAI, AutoGen, MCP, custom).
15+
16+
```json
17+
{
18+
"agent_id": "01HZQK3P8EMXR9V7T5N2W4J6C0",
19+
"name": "WebSearchAgent",
20+
"version": "1.0.0",
21+
"capabilities": [
22+
{"id": "web.search", "description": "Search the web for current information."},
23+
{"id": "web.scrape", "description": "Extract content from web pages."}
24+
],
25+
"endpoint": {
26+
"protocol": "https",
27+
"url": "https://my-agent.example.com/api"
28+
},
29+
"pricing": {
30+
"base_cost_joules": 2.854e-21
31+
}
32+
}
33+
```
34+
35+
## Installation
36+
37+
```bash
38+
pip install agentcard-mcp
39+
```
40+
41+
## Quick Start — Claude Desktop
42+
43+
Add to `~/Library/Application Support/Claude/claude_desktop_config.json`:
44+
45+
```json
46+
{
47+
"mcpServers": {
48+
"agentcard": {
49+
"command": "python",
50+
"args": ["-m", "agentcard_mcp"]
51+
}
52+
}
53+
}
54+
```
55+
56+
Then restart Claude Desktop and ask:
57+
58+
> *"Register my identity as a code assistant using AgentCard."*
59+
60+
## Tools
61+
62+
### `agentcard_declare`
63+
Register your AgentCard identity for this session.
64+
65+
```
66+
Input: card_json (string) — JSON conforming to AgentCard v1.0 schema.
67+
68+
Required fields:
69+
agent_id — 26-char Crockford Base32 ULID (e.g. 01HZQK3P8EMXR9V7T5N2W4J6C0)
70+
name — Display name (1–128 chars)
71+
version — Semantic version (e.g. "1.0.0")
72+
capabilities — Array with ≥1 entry, each with dot-namespaced "id"
73+
endpoint — { "protocol": "https"|"http"|"grpc"|"stdio"|"mcp", "url": "..." }
74+
```
75+
76+
### `agentcard_resolve`
77+
Look up a registered agent's AgentCard by name or agent_id.
78+
79+
```
80+
Input: query (string) — partial name (case-insensitive) or exact 26-char ULID.
81+
```
82+
83+
### `agentcard_validate`
84+
Validate any JSON against the AgentCard v1.0 schema.
85+
86+
Checks:
87+
- 26-char Crockford Base32 ULID format
88+
- Semver 2.0 version string
89+
- Dot-namespaced capability ids (`^[a-z0-9][a-z0-9._-]*$`)
90+
- Landauer floor physics check on pricing (`base_cost_joules ≥ 2.854e-21 J`)
91+
92+
### `agentcard_list`
93+
List all AgentCards registered in this session.
94+
95+
## Resources
96+
97+
| URI | Description |
98+
|-----|-------------|
99+
| `agentcard://schema` | Canonical AgentCard v1.0 JSON Schema |
100+
| `agentcard://registry` | All declared cards as JSON array |
101+
102+
## Usage Examples
103+
104+
### Declare an identity
105+
```
106+
User: Register my identity as a data analysis agent.
107+
108+
Claude uses agentcard_declare({
109+
"agent_id": "01HZQK3P8EMXR9V7T5N2W4J6C0",
110+
"name": "DataAnalysisAgent",
111+
"version": "1.0.0",
112+
"capabilities": [
113+
{"id": "data.analyze", "description": "Analyze datasets and produce insights."},
114+
{"id": "data.visualize", "description": "Create charts and visualizations."}
115+
],
116+
"endpoint": {"protocol": "mcp", "url": "mcp://claude-desktop/data-agent"}
117+
})
118+
→ ✓ AgentCard declared — agent_id=01HZQK3P8EMXR9V7T5N2W4J6C0, name='DataAnalysisAgent', capabilities=2
119+
```
120+
121+
### Validate a peer's card
122+
```
123+
User: Is this AgentCard valid? [paste JSON]
124+
125+
Claude uses agentcard_validate(card_json)
126+
→ VALID ✓
127+
agent_id : 01HZQK3P8EMXR9V7T5N2W4J6C0
128+
capabilities: 2 — [data.analyze, data.visualize]
129+
endpoint : mcp://claude-desktop/data-agent
130+
```
131+
132+
### Resolve a peer agent
133+
```
134+
User: What can the researcher agent do?
135+
136+
Claude uses agentcard_resolve("researcher")
137+
→ { "agent_id": "...", "capabilities": [...], ... }
138+
```
139+
140+
## AgentCard Schema Highlights
141+
142+
| Field | Type | Description |
143+
|-------|------|-------------|
144+
| `agent_id` | string | 26-char Crockford Base32 ULID — globally unique |
145+
| `name` | string | Display name 1–128 chars |
146+
| `version` | string | Semantic version (semver 2.0) |
147+
| `capabilities[].id` | string | Dot-namespaced (`web.search`, `tool.python`) |
148+
| `endpoint.protocol` | enum | `http`, `https`, `grpc`, `stdio`, `mcp` |
149+
| `pricing.base_cost_joules` | float | ≥ Landauer floor (2.854e-21 J) or 0 |
150+
| `metadata.pacr:trust_tier` | enum | `untrusted \| basic \| established \| verified \| banned` |
151+
152+
Full schema: [`agentcard://schema`](../../schema.json)
153+
154+
## Framework Adapters
155+
156+
| Framework | Package | Import |
157+
|-----------|---------|--------|
158+
| LangChain | `pip install agentcard-adapters[langchain]` | `from agentcard_adapters import tool_to_agentcard` |
159+
| CrewAI | `pip install agentcard-adapters[crewai]` | `from agentcard_adapters import agent_to_agentcard` |
160+
| AutoGen | `pip install agentcard-adapters[autogen]` | `from agentcard_adapters.autogen_adapter import agent_to_agentcard` |
161+
162+
## Development
163+
164+
```bash
165+
pip install -e ".[dev]"
166+
pytest tests/
167+
```
168+
169+
## License
170+
171+
Apache 2.0 + CC-BY 4.0 (spec).
172+
Patent non-reservation: [NOTICE](../../NOTICE).
173+
174+
## References
175+
176+
- [AgentCard Specification](https://github.com/kwailapt/AgentCard)
177+
- [JSON Schema](https://github.com/kwailapt/AgentCard/blob/main/schema.json)
178+
- [Model Context Protocol](https://modelcontextprotocol.io)
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
[build-system]
2+
requires = ["hatchling"]
3+
build-backend = "hatchling.build"
4+
5+
[project]
6+
name = "agentcard-mcp"
7+
version = "0.1.0"
8+
description = "MCP server for AgentCard v1.0 — the framework-neutral A2A identity standard"
9+
readme = "README.md"
10+
license = { text = "Apache-2.0" }
11+
authors = [{ name = "kwailapt", email = "kwailapt@users.noreply.github.com" }]
12+
keywords = ["mcp", "agent", "a2a", "agentcard", "identity", "llm"]
13+
classifiers = [
14+
"Development Status :: 3 - Alpha",
15+
"Intended Audience :: Developers",
16+
"License :: OSI Approved :: Apache Software License",
17+
"Programming Language :: Python :: 3",
18+
"Programming Language :: Python :: 3.10",
19+
"Programming Language :: Python :: 3.11",
20+
"Programming Language :: Python :: 3.12",
21+
"Topic :: Scientific/Engineering :: Artificial Intelligence",
22+
"Topic :: Software Development :: Libraries :: Python Modules",
23+
]
24+
requires-python = ">=3.10"
25+
26+
# Zero mandatory runtime deps — only 'mcp' is needed for the server
27+
dependencies = [
28+
"mcp>=1.0.0",
29+
]
30+
31+
[project.optional-dependencies]
32+
dev = [
33+
"pytest>=8",
34+
"pytest-asyncio>=0.23",
35+
]
36+
37+
[project.scripts]
38+
agentcard-mcp = "agentcard_mcp.__main__:main"
39+
40+
[project.urls]
41+
Homepage = "https://github.com/kwailapt/AgentCard"
42+
Repository = "https://github.com/kwailapt/AgentCard"
43+
"Bug Tracker" = "https://github.com/kwailapt/AgentCard/issues"
44+
Documentation = "https://github.com/kwailapt/AgentCard/tree/main/adapters/python/mcp-server"
45+
46+
[tool.hatch.build.targets.wheel]
47+
packages = ["src/agentcard_mcp"]
48+
49+
[tool.pytest.ini_options]
50+
asyncio_mode = "auto"
51+
testpaths = ["tests"]
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
{
2+
"$schema": "https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json",
3+
"name": "io.github.kwailapt/agentcard",
4+
"description": "AgentCard v1.0 — framework-neutral identity layer for agent-to-agent (A2A) communication. Declare, resolve, and validate agent identities using the open AgentCard standard. Analogous to HTTP headers for the agent web.",
5+
"repository": {
6+
"url": "https://github.com/kwailapt/AgentCard",
7+
"source": "github"
8+
},
9+
"version_detail": {
10+
"version": "0.1.0"
11+
},
12+
"packages": [
13+
{
14+
"registry_type": "pypi",
15+
"name": "agentcard-mcp",
16+
"version": "0.1.0",
17+
"package_arguments": [
18+
{
19+
"type": "positional",
20+
"value": "-m agentcard_mcp",
21+
"value_hint": "Module entry point"
22+
}
23+
],
24+
"environment_variables": []
25+
}
26+
],
27+
"tools": [
28+
{
29+
"name": "agentcard_declare",
30+
"description": "Register an agent's AgentCard identity (JSON conforming to AgentCard v1.0 schema). Stores in session registry."
31+
},
32+
{
33+
"name": "agentcard_resolve",
34+
"description": "Look up a registered agent's AgentCard by name (partial match) or exact 26-char Crockford Base32 ULID."
35+
},
36+
{
37+
"name": "agentcard_validate",
38+
"description": "Validate any JSON string against the AgentCard v1.0 schema. Checks ULID format, semver version, capability ids, Landauer floor pricing."
39+
},
40+
{
41+
"name": "agentcard_list",
42+
"description": "List all AgentCards registered in this session with ids, names, capability counts, and endpoints."
43+
}
44+
],
45+
"resources": [
46+
{
47+
"uri": "agentcard://schema",
48+
"description": "The canonical AgentCard v1.0 JSON Schema."
49+
},
50+
{
51+
"uri": "agentcard://registry",
52+
"description": "All declared AgentCards in this session as a JSON array."
53+
}
54+
]
55+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
"""
2+
agentcard-mcp
3+
=============
4+
5+
MCP server that gives any Claude / LLM agent an AgentCard v1.0 identity.
6+
7+
Tools
8+
-----
9+
- ``agentcard_declare`` — register your identity (stores in-session)
10+
- ``agentcard_resolve`` — look up a peer agent by name or id
11+
- ``agentcard_validate`` — validate any JSON string as a valid AgentCard
12+
- ``agentcard_list`` — list all registered agents
13+
14+
Resources
15+
---------
16+
- ``agentcard://schema`` — the canonical JSON Schema (v1.0)
17+
- ``agentcard://registry`` — all declared cards as JSON array
18+
19+
AgentCard standard: https://github.com/kwailapt/AgentCard
20+
Apache 2.0. Patent non-reservation: see NOTICE.
21+
"""
22+
23+
__version__ = "0.1.0"
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
"""
2+
agentcard_mcp.__main__
3+
======================
4+
5+
Entry point for ``python -m agentcard_mcp``.
6+
7+
Usage:
8+
python -m agentcard_mcp # stdio transport (default)
9+
python -m agentcard_mcp --http # HTTP/SSE on port 8890
10+
python -m agentcard_mcp --port 9000 # HTTP/SSE on custom port
11+
"""
12+
13+
import sys
14+
15+
from .server import mcp
16+
17+
18+
def main() -> None:
19+
args = sys.argv[1:]
20+
if "--http" in args:
21+
port_idx = args.index("--port") if "--port" in args else -1
22+
port = int(args[port_idx + 1]) if port_idx >= 0 else 8890
23+
mcp.run(transport="sse", host="0.0.0.0", port=port)
24+
else:
25+
mcp.run(transport="stdio")
26+
27+
28+
if __name__ == "__main__":
29+
main()

0 commit comments

Comments
 (0)