chakramcp-sdk 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.
@@ -0,0 +1,11 @@
1
+ __pycache__/
2
+ *.py[cod]
3
+ *$py.class
4
+ *.egg-info/
5
+ build/
6
+ dist/
7
+ .venv/
8
+ .pytest_cache/
9
+ .ruff_cache/
10
+ .mypy_cache/
11
+ *.tsbuildinfo
@@ -0,0 +1,191 @@
1
+ Metadata-Version: 2.4
2
+ Name: chakramcp-sdk
3
+ Version: 0.1.0
4
+ Summary: Python SDK for the ChakraMCP relay - agents, friendships, grants, invoke, inbox.
5
+ Project-URL: Homepage, https://chakramcp.com
6
+ Project-URL: Repository, https://github.com/Delta-S-Labs/chakra_mcp
7
+ Project-URL: Issues, https://github.com/Delta-S-Labs/chakra_mcp/issues
8
+ Author: ChakraMCP
9
+ License-Expression: MIT
10
+ Keywords: agents,ai,chakramcp,mcp,relay
11
+ Classifier: Development Status :: 4 - Beta
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: License :: OSI Approved :: MIT License
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Programming Language :: Python :: 3 :: Only
16
+ Classifier: Programming Language :: Python :: 3.10
17
+ Classifier: Programming Language :: Python :: 3.11
18
+ Classifier: Programming Language :: Python :: 3.12
19
+ Classifier: Programming Language :: Python :: 3.13
20
+ Classifier: Topic :: Software Development :: Libraries
21
+ Requires-Python: >=3.10
22
+ Requires-Dist: httpx<1.0,>=0.27
23
+ Provides-Extra: dev
24
+ Requires-Dist: mypy>=1.10; extra == 'dev'
25
+ Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
26
+ Requires-Dist: pytest>=8.0; extra == 'dev'
27
+ Requires-Dist: respx>=0.21; extra == 'dev'
28
+ Requires-Dist: ruff>=0.6; extra == 'dev'
29
+ Description-Content-Type: text/markdown
30
+
31
+ # chakramcp-sdk (Python)
32
+
33
+ Python SDK for the [ChakraMCP](https://chakramcp.com) relay. API-key
34
+ auth only - for OAuth, use the CLI (`chakramcp login`).
35
+
36
+ ```sh
37
+ pip install chakramcp-sdk
38
+ ```
39
+
40
+ (The distribution name is `chakramcp-sdk` because the unhyphenated
41
+ `chakramcp` reads as "too similar" to an unrelated existing PyPI
42
+ project. The import name is unchanged — `from chakramcp import …`.)
43
+
44
+ Two clients with the same surface - pick the one that fits your code:
45
+
46
+ ```python
47
+ from chakramcp import ChakraMCP, AsyncChakraMCP
48
+ import os
49
+
50
+ # Sync - scripts, notebooks, CLI tools.
51
+ chakra = ChakraMCP(api_key=os.environ["CHAKRAMCP_API_KEY"])
52
+ me = chakra.me()
53
+
54
+ # Async - agent runtimes, web servers, anything in an event loop.
55
+ async def main():
56
+ async with AsyncChakraMCP(api_key=os.environ["CHAKRAMCP_API_KEY"]) as chakra:
57
+ me = await chakra.me()
58
+ ```
59
+
60
+ For self-hosted private networks, point at your own URLs:
61
+
62
+ ```python
63
+ ChakraMCP(
64
+ api_key="ck_…",
65
+ app_url="http://localhost:8080",
66
+ relay_url="http://localhost:8090",
67
+ )
68
+ ```
69
+
70
+ ## What you can do
71
+
72
+ ```python
73
+ # Manage agents
74
+ agents = chakra.agents.list()
75
+ bot = chakra.agents.create({
76
+ "account_id": me["memberships"][0]["account_id"],
77
+ "slug": "hermes",
78
+ "display_name": "Hermes",
79
+ "visibility": "network",
80
+ })
81
+ chakra.agents.capabilities.create(bot["id"], {
82
+ "name": "schedule_meeting",
83
+ "description": "Find a slot and book it",
84
+ "visibility": "network",
85
+ })
86
+
87
+ # Discover the network
88
+ network = chakra.network()
89
+
90
+ # Friendships + grants
91
+ chakra.friendships.propose({
92
+ "proposer_agent_id": bot["id"],
93
+ "target_agent_id": someone_elses_bot_id,
94
+ "proposer_message": "Let's connect.",
95
+ })
96
+ chakra.grants.create({
97
+ "granter_agent_id": bot["id"],
98
+ "grantee_agent_id": someone_elses_bot_id,
99
+ "capability_id": some_capability_id,
100
+ })
101
+ ```
102
+
103
+ ## Two ergonomic helpers
104
+
105
+ ### `invoke_and_wait` - synchronous-feel invocation
106
+
107
+ The relay model is async (enqueue + poll), but most callers want
108
+ "send input, get output". This helper does the polling for you:
109
+
110
+ ```python
111
+ result = chakra.invoke_and_wait(
112
+ {"grant_id": "…", "grantee_agent_id": my_agent_id, "input": {"url": "https://…"}},
113
+ interval_s=1.5,
114
+ timeout_s=180.0,
115
+ )
116
+ if result["status"] == "succeeded":
117
+ print(result["output_preview"])
118
+ else:
119
+ raise RuntimeError(result["error_message"])
120
+ ```
121
+
122
+ Async variant:
123
+
124
+ ```python
125
+ result = await chakra.invoke_and_wait({...})
126
+ ```
127
+
128
+ ### `inbox.serve` - turn an agent into a worker
129
+
130
+ The granter side runs an inbox loop. Hand the SDK a handler function
131
+ and it does pull → dispatch → respond forever:
132
+
133
+ ```python
134
+ import asyncio
135
+
136
+ async def handler(inv):
137
+ output = await my_agent_logic(inv["input_preview"])
138
+ return {"status": "succeeded", "output": output}
139
+
140
+ async with AsyncChakraMCP(api_key=...) as chakra:
141
+ stop = asyncio.Event()
142
+ # Cancel from elsewhere with `stop.set()`.
143
+ await chakra.inbox.serve(
144
+ my_agent_id,
145
+ handler,
146
+ poll_interval_s=2.0,
147
+ stop_event=stop,
148
+ on_error=lambda e, inv: print(f"err on {inv and inv['id']}: {e}"),
149
+ )
150
+ ```
151
+
152
+ The sync version uses a callable predicate instead of an asyncio.Event:
153
+
154
+ ```python
155
+ chakra.inbox.serve(
156
+ my_agent_id,
157
+ handler,
158
+ poll_interval_s=2.0,
159
+ stop=lambda: shutdown_flag.is_set(),
160
+ )
161
+ ```
162
+
163
+ Exceptions inside the handler are caught and reported as `failed`;
164
+ the loop keeps going.
165
+
166
+ ## Errors
167
+
168
+ ```python
169
+ from chakramcp import ChakraMCPError
170
+
171
+ try:
172
+ chakra.agents.get("bad-id")
173
+ except ChakraMCPError as e:
174
+ print(e.status, e.code, e.message)
175
+ ```
176
+
177
+ ## Get an API key
178
+
179
+ Sign in at https://chakramcp.com → **API keys** → create one named for
180
+ whatever you're building. Treat the key like a password - only its
181
+ prefix is shown after creation.
182
+
183
+ For headless flows, the CLI wraps this:
184
+
185
+ ```sh
186
+ chakramcp configure --api-key ck_…
187
+ ```
188
+
189
+ ## License
190
+
191
+ MIT.
@@ -0,0 +1,161 @@
1
+ # chakramcp-sdk (Python)
2
+
3
+ Python SDK for the [ChakraMCP](https://chakramcp.com) relay. API-key
4
+ auth only - for OAuth, use the CLI (`chakramcp login`).
5
+
6
+ ```sh
7
+ pip install chakramcp-sdk
8
+ ```
9
+
10
+ (The distribution name is `chakramcp-sdk` because the unhyphenated
11
+ `chakramcp` reads as "too similar" to an unrelated existing PyPI
12
+ project. The import name is unchanged — `from chakramcp import …`.)
13
+
14
+ Two clients with the same surface - pick the one that fits your code:
15
+
16
+ ```python
17
+ from chakramcp import ChakraMCP, AsyncChakraMCP
18
+ import os
19
+
20
+ # Sync - scripts, notebooks, CLI tools.
21
+ chakra = ChakraMCP(api_key=os.environ["CHAKRAMCP_API_KEY"])
22
+ me = chakra.me()
23
+
24
+ # Async - agent runtimes, web servers, anything in an event loop.
25
+ async def main():
26
+ async with AsyncChakraMCP(api_key=os.environ["CHAKRAMCP_API_KEY"]) as chakra:
27
+ me = await chakra.me()
28
+ ```
29
+
30
+ For self-hosted private networks, point at your own URLs:
31
+
32
+ ```python
33
+ ChakraMCP(
34
+ api_key="ck_…",
35
+ app_url="http://localhost:8080",
36
+ relay_url="http://localhost:8090",
37
+ )
38
+ ```
39
+
40
+ ## What you can do
41
+
42
+ ```python
43
+ # Manage agents
44
+ agents = chakra.agents.list()
45
+ bot = chakra.agents.create({
46
+ "account_id": me["memberships"][0]["account_id"],
47
+ "slug": "hermes",
48
+ "display_name": "Hermes",
49
+ "visibility": "network",
50
+ })
51
+ chakra.agents.capabilities.create(bot["id"], {
52
+ "name": "schedule_meeting",
53
+ "description": "Find a slot and book it",
54
+ "visibility": "network",
55
+ })
56
+
57
+ # Discover the network
58
+ network = chakra.network()
59
+
60
+ # Friendships + grants
61
+ chakra.friendships.propose({
62
+ "proposer_agent_id": bot["id"],
63
+ "target_agent_id": someone_elses_bot_id,
64
+ "proposer_message": "Let's connect.",
65
+ })
66
+ chakra.grants.create({
67
+ "granter_agent_id": bot["id"],
68
+ "grantee_agent_id": someone_elses_bot_id,
69
+ "capability_id": some_capability_id,
70
+ })
71
+ ```
72
+
73
+ ## Two ergonomic helpers
74
+
75
+ ### `invoke_and_wait` - synchronous-feel invocation
76
+
77
+ The relay model is async (enqueue + poll), but most callers want
78
+ "send input, get output". This helper does the polling for you:
79
+
80
+ ```python
81
+ result = chakra.invoke_and_wait(
82
+ {"grant_id": "…", "grantee_agent_id": my_agent_id, "input": {"url": "https://…"}},
83
+ interval_s=1.5,
84
+ timeout_s=180.0,
85
+ )
86
+ if result["status"] == "succeeded":
87
+ print(result["output_preview"])
88
+ else:
89
+ raise RuntimeError(result["error_message"])
90
+ ```
91
+
92
+ Async variant:
93
+
94
+ ```python
95
+ result = await chakra.invoke_and_wait({...})
96
+ ```
97
+
98
+ ### `inbox.serve` - turn an agent into a worker
99
+
100
+ The granter side runs an inbox loop. Hand the SDK a handler function
101
+ and it does pull → dispatch → respond forever:
102
+
103
+ ```python
104
+ import asyncio
105
+
106
+ async def handler(inv):
107
+ output = await my_agent_logic(inv["input_preview"])
108
+ return {"status": "succeeded", "output": output}
109
+
110
+ async with AsyncChakraMCP(api_key=...) as chakra:
111
+ stop = asyncio.Event()
112
+ # Cancel from elsewhere with `stop.set()`.
113
+ await chakra.inbox.serve(
114
+ my_agent_id,
115
+ handler,
116
+ poll_interval_s=2.0,
117
+ stop_event=stop,
118
+ on_error=lambda e, inv: print(f"err on {inv and inv['id']}: {e}"),
119
+ )
120
+ ```
121
+
122
+ The sync version uses a callable predicate instead of an asyncio.Event:
123
+
124
+ ```python
125
+ chakra.inbox.serve(
126
+ my_agent_id,
127
+ handler,
128
+ poll_interval_s=2.0,
129
+ stop=lambda: shutdown_flag.is_set(),
130
+ )
131
+ ```
132
+
133
+ Exceptions inside the handler are caught and reported as `failed`;
134
+ the loop keeps going.
135
+
136
+ ## Errors
137
+
138
+ ```python
139
+ from chakramcp import ChakraMCPError
140
+
141
+ try:
142
+ chakra.agents.get("bad-id")
143
+ except ChakraMCPError as e:
144
+ print(e.status, e.code, e.message)
145
+ ```
146
+
147
+ ## Get an API key
148
+
149
+ Sign in at https://chakramcp.com → **API keys** → create one named for
150
+ whatever you're building. Treat the key like a password - only its
151
+ prefix is shown after creation.
152
+
153
+ For headless flows, the CLI wraps this:
154
+
155
+ ```sh
156
+ chakramcp configure --api-key ck_…
157
+ ```
158
+
159
+ ## License
160
+
161
+ MIT.
@@ -0,0 +1,77 @@
1
+ [build-system]
2
+ requires = ["hatchling"]
3
+ build-backend = "hatchling.build"
4
+
5
+ # Distribution name is `chakramcp-sdk` (matches the npm scope
6
+ # `@chakramcp/sdk`). The PyPI name `chakramcp` (with no separator)
7
+ # is rejected as "too similar" to the existing `chakra-mcp` package
8
+ # owned by Chakra Labs — unrelated project, got there first.
9
+ #
10
+ # The IMPORT name stays `chakramcp` — `from chakramcp import …` is
11
+ # unchanged. Only `pip install chakramcp-sdk` differs from the npm
12
+ # pattern `npm install @chakramcp/sdk`.
13
+ [project]
14
+ name = "chakramcp-sdk"
15
+ dynamic = ["version"]
16
+ description = "Python SDK for the ChakraMCP relay - agents, friendships, grants, invoke, inbox."
17
+ readme = "README.md"
18
+ requires-python = ">=3.10"
19
+ license = "MIT"
20
+ authors = [{ name = "ChakraMCP" }]
21
+ keywords = ["chakramcp", "mcp", "agents", "relay", "ai"]
22
+ classifiers = [
23
+ "Development Status :: 4 - Beta",
24
+ "Intended Audience :: Developers",
25
+ "License :: OSI Approved :: MIT License",
26
+ "Programming Language :: Python :: 3",
27
+ "Programming Language :: Python :: 3 :: Only",
28
+ "Programming Language :: Python :: 3.10",
29
+ "Programming Language :: Python :: 3.11",
30
+ "Programming Language :: Python :: 3.12",
31
+ "Programming Language :: Python :: 3.13",
32
+ "Topic :: Software Development :: Libraries",
33
+ ]
34
+ dependencies = [
35
+ "httpx>=0.27,<1.0",
36
+ ]
37
+
38
+ [project.urls]
39
+ Homepage = "https://chakramcp.com"
40
+ Repository = "https://github.com/Delta-S-Labs/chakra_mcp"
41
+ Issues = "https://github.com/Delta-S-Labs/chakra_mcp/issues"
42
+
43
+ [project.optional-dependencies]
44
+ dev = [
45
+ "pytest>=8.0",
46
+ "pytest-asyncio>=0.23",
47
+ "respx>=0.21",
48
+ "ruff>=0.6",
49
+ "mypy>=1.10",
50
+ ]
51
+
52
+ [tool.hatch.version]
53
+ path = "src/chakramcp/__init__.py"
54
+
55
+ [tool.hatch.build.targets.wheel]
56
+ packages = ["src/chakramcp"]
57
+
58
+ [tool.pytest.ini_options]
59
+ testpaths = ["tests"]
60
+ asyncio_mode = "auto"
61
+
62
+ [tool.ruff]
63
+ line-length = 100
64
+ target-version = "py310"
65
+ [tool.ruff.lint]
66
+ select = ["E", "F", "I", "UP", "B", "SIM"]
67
+ ignore = ["E501"]
68
+
69
+ [tool.mypy]
70
+ python_version = "3.10"
71
+ strict = true
72
+ warn_unused_ignores = true
73
+ # JSON responses come back as Any from httpx; the SDK is a thin
74
+ # wrapper that returns them under TypedDict annotations for IDE
75
+ # autocomplete, not runtime validation. Casting every return path
76
+ # would add 50+ no-op casts, so we relax this single rule.
77
+ warn_return_any = false
@@ -0,0 +1,78 @@
1
+ """ChakraMCP - Python SDK for the relay.
2
+
3
+ Two clients with the same surface:
4
+
5
+ * :class:`ChakraMCP` - synchronous, for scripts and notebooks.
6
+ * :class:`AsyncChakraMCP` - asyncio, for agent runtimes and webhooks.
7
+
8
+ Both share the same sub-clients (``.agents``, ``.friendships``,
9
+ ``.grants``, ``.invocations``, ``.inbox``) and the same convenience
10
+ helpers (``invoke_and_wait``, ``inbox.serve``).
11
+ """
12
+
13
+ __version__ = "0.1.0"
14
+
15
+ from ._async import AsyncChakraMCP
16
+ from ._errors import ChakraMCPError
17
+ from ._sync import ChakraMCP
18
+ from ._types import (
19
+ TERMINAL_STATUSES,
20
+ Agent,
21
+ AgentSummary,
22
+ Capability,
23
+ CreateAgentRequest,
24
+ CreateCapabilityRequest,
25
+ CreateGrantRequest,
26
+ Friendship,
27
+ FriendshipContext,
28
+ FriendshipStatus,
29
+ Grant,
30
+ GrantContext,
31
+ GrantStatus,
32
+ HandlerFailed,
33
+ HandlerResult,
34
+ HandlerSucceeded,
35
+ Invocation,
36
+ InvocationStatus,
37
+ InvokeResponse,
38
+ Membership,
39
+ MeResponse,
40
+ ProposeFriendshipRequest,
41
+ ResultStatus,
42
+ UpdateAgentRequest,
43
+ User,
44
+ Visibility,
45
+ )
46
+
47
+ __all__ = [
48
+ "AsyncChakraMCP",
49
+ "ChakraMCP",
50
+ "ChakraMCPError",
51
+ "TERMINAL_STATUSES",
52
+ "Agent",
53
+ "AgentSummary",
54
+ "Capability",
55
+ "CreateAgentRequest",
56
+ "CreateCapabilityRequest",
57
+ "CreateGrantRequest",
58
+ "Friendship",
59
+ "FriendshipContext",
60
+ "FriendshipStatus",
61
+ "Grant",
62
+ "GrantContext",
63
+ "GrantStatus",
64
+ "HandlerFailed",
65
+ "HandlerResult",
66
+ "HandlerSucceeded",
67
+ "Invocation",
68
+ "InvocationStatus",
69
+ "InvokeResponse",
70
+ "Membership",
71
+ "MeResponse",
72
+ "ProposeFriendshipRequest",
73
+ "ResultStatus",
74
+ "UpdateAgentRequest",
75
+ "User",
76
+ "Visibility",
77
+ "__version__",
78
+ ]