server4agent 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,6 @@
1
+ .venv/
2
+ __pycache__/
3
+ *.egg-info/
4
+ dist/
5
+ build/
6
+ .pytest_cache/
@@ -0,0 +1,18 @@
1
+ # Changelog
2
+
3
+ ## 0.1.0
4
+
5
+ Initial release.
6
+
7
+ - Sync `Server4Agent` and async `AsyncServer4Agent` clients.
8
+ - Resource-oriented handles: `create()`/`get()` return `Server`/`Project`/`Task`/`Build`
9
+ objects with scoped sub-resources (`server.tasks`, `server.files`, ...) and actions
10
+ (`server.exec()`, `server.deploy()`, `project.update()`).
11
+ - `task.wait()` / `build.wait()` poll until a terminal state, with an `on_poll` callback.
12
+ - Typed error hierarchy under `Server4AgentError` (`NotFoundError`, `RateLimitError`,
13
+ `APIStatusError`, ...) exposing `status`, `code`, and `request_id`.
14
+ - Automatic retries with exponential backoff + jitter; retries are method-aware so a
15
+ `POST` is never retried in a way that could double-create. Configurable `timeout`
16
+ and `max_retries`.
17
+ - Webhook signature verification (`verify_webhook_signature`).
18
+ - Ships `py.typed`. Opt-in request logging via `SERVER4AGENT_LOG=debug`.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Server4Agent
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,151 @@
1
+ Metadata-Version: 2.4
2
+ Name: server4agent
3
+ Version: 0.1.0
4
+ Summary: Python client for the Server4Agent REST API — sync + async, typed, with retries.
5
+ Project-URL: Homepage, https://www.server4agent.com
6
+ Project-URL: Documentation, https://www.server4agent.com/docs/sdks
7
+ Project-URL: Repository, https://github.com/Server4Agent/server4agent-python
8
+ License: MIT
9
+ License-File: LICENSE
10
+ Keywords: agents,mcp,sdk,server4agent
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: Typing :: Typed
17
+ Requires-Python: >=3.9
18
+ Requires-Dist: httpx<1,>=0.24
19
+ Provides-Extra: dev
20
+ Requires-Dist: pytest>=7; extra == 'dev'
21
+ Description-Content-Type: text/markdown
22
+
23
+ # server4agent
24
+
25
+ Python client for the [Server4Agent](https://www.server4agent.com) REST API —
26
+ sync **and** async, fully typed, with automatic retries.
27
+
28
+ This SDK is for the code *around* your agent — your backend, a script, a
29
+ notebook, a webhook receiver. If your agent itself does tool-calling, point it
30
+ at the [MCP server](https://www.server4agent.com/docs/mcp) directly; it doesn't
31
+ need this package.
32
+
33
+ ## Install
34
+
35
+ ```bash
36
+ pip install server4agent
37
+ ```
38
+
39
+ Requires Python 3.9+. Depends only on [`httpx`](https://www.python-httpx.org/).
40
+
41
+ ## Quickstart
42
+
43
+ ```python
44
+ from server4agent import Server4Agent
45
+
46
+ client = Server4Agent() # reads SERVER4AGENT_API_KEY from the environment
47
+
48
+ # create() returns a handle you can act on directly
49
+ server = client.servers.create(tier="small")
50
+
51
+ # kick off a build and block until it's live
52
+ build = server.builds.start("a FastAPI todo API with a web UI").wait()
53
+ print(build.url)
54
+
55
+ # run a command right on the server
56
+ print(server.exec("ls -la"))
57
+ ```
58
+
59
+ `api_key` can also be passed explicitly: `Server4Agent(api_key="sk_live_...")`.
60
+ Use it as a context manager to close the connection pool:
61
+
62
+ ```python
63
+ with Server4Agent() as client:
64
+ ...
65
+ ```
66
+
67
+ ## Async
68
+
69
+ The async client mirrors the sync one method-for-method:
70
+
71
+ ```python
72
+ import asyncio
73
+ from server4agent import AsyncServer4Agent
74
+
75
+ async def main():
76
+ async with AsyncServer4Agent() as client:
77
+ server = await client.servers.create(tier="small")
78
+ task = await server.tasks.create("Scrape today's HN front page to JSON.")
79
+ await task.wait()
80
+ print(task.status, task.result)
81
+
82
+ asyncio.run(main())
83
+ ```
84
+
85
+ ## Handles
86
+
87
+ `create()` and `get()` return rich handles — data records you can also act on:
88
+
89
+ ```python
90
+ server = client.servers.get("srv_abc")
91
+ server.tasks.create("...") # sub-resources are scoped to the server
92
+ server.files.write("app.py", "...")
93
+ server.deploy()
94
+ server.refresh() # re-fetch in place
95
+
96
+ project = client.projects.get("prj_xyz")
97
+ project.update(visibility="public")
98
+ ```
99
+
100
+ `task.wait()` / `build.wait()` poll until a terminal state; both accept
101
+ `poll_interval` and `timeout` (seconds).
102
+
103
+ ## Errors
104
+
105
+ Every failure is a subclass of `Server4AgentError`, so you can catch broadly or
106
+ narrowly:
107
+
108
+ ```python
109
+ from server4agent import NotFoundError, RateLimitError, APIStatusError
110
+
111
+ try:
112
+ client.servers.get("srv_missing")
113
+ except NotFoundError:
114
+ ... # 404
115
+ except RateLimitError as e:
116
+ ... # 429 (already retried; quote e.request_id to support)
117
+ except APIStatusError as e:
118
+ print(e.status, e.code, e.message, e.request_id)
119
+ ```
120
+
121
+ Retryable failures (429, 5xx, network blips) are retried automatically with
122
+ exponential backoff. Tune it per client:
123
+
124
+ ```python
125
+ Server4Agent(timeout=30.0, max_retries=4)
126
+ ```
127
+
128
+ ## Verifying webhooks
129
+
130
+ ```python
131
+ import os
132
+ from server4agent import verify_webhook_signature
133
+
134
+ # request.data must be the raw body — parse it as JSON only after verifying.
135
+ ok = verify_webhook_signature(
136
+ secret=os.environ["SERVER4AGENT_WEBHOOK_SECRET"],
137
+ body=request.data.decode("utf-8"),
138
+ header=request.headers.get("Server4Agent-Signature"),
139
+ )
140
+ if not ok:
141
+ return "invalid signature", 401
142
+ ```
143
+
144
+ ## API surface
145
+
146
+ `servers`, `projects`, `templates`, `tasks`, `builds`, `files`, `keys`,
147
+ `webhooks` — see the [REST API docs](https://www.server4agent.com/docs/api) for
148
+ the endpoints each method wraps.
149
+
150
+ Server-side only: this SDK holds your `sk_live_` key. Never embed it in a
151
+ notebook or script you share.
@@ -0,0 +1,129 @@
1
+ # server4agent
2
+
3
+ Python client for the [Server4Agent](https://www.server4agent.com) REST API —
4
+ sync **and** async, fully typed, with automatic retries.
5
+
6
+ This SDK is for the code *around* your agent — your backend, a script, a
7
+ notebook, a webhook receiver. If your agent itself does tool-calling, point it
8
+ at the [MCP server](https://www.server4agent.com/docs/mcp) directly; it doesn't
9
+ need this package.
10
+
11
+ ## Install
12
+
13
+ ```bash
14
+ pip install server4agent
15
+ ```
16
+
17
+ Requires Python 3.9+. Depends only on [`httpx`](https://www.python-httpx.org/).
18
+
19
+ ## Quickstart
20
+
21
+ ```python
22
+ from server4agent import Server4Agent
23
+
24
+ client = Server4Agent() # reads SERVER4AGENT_API_KEY from the environment
25
+
26
+ # create() returns a handle you can act on directly
27
+ server = client.servers.create(tier="small")
28
+
29
+ # kick off a build and block until it's live
30
+ build = server.builds.start("a FastAPI todo API with a web UI").wait()
31
+ print(build.url)
32
+
33
+ # run a command right on the server
34
+ print(server.exec("ls -la"))
35
+ ```
36
+
37
+ `api_key` can also be passed explicitly: `Server4Agent(api_key="sk_live_...")`.
38
+ Use it as a context manager to close the connection pool:
39
+
40
+ ```python
41
+ with Server4Agent() as client:
42
+ ...
43
+ ```
44
+
45
+ ## Async
46
+
47
+ The async client mirrors the sync one method-for-method:
48
+
49
+ ```python
50
+ import asyncio
51
+ from server4agent import AsyncServer4Agent
52
+
53
+ async def main():
54
+ async with AsyncServer4Agent() as client:
55
+ server = await client.servers.create(tier="small")
56
+ task = await server.tasks.create("Scrape today's HN front page to JSON.")
57
+ await task.wait()
58
+ print(task.status, task.result)
59
+
60
+ asyncio.run(main())
61
+ ```
62
+
63
+ ## Handles
64
+
65
+ `create()` and `get()` return rich handles — data records you can also act on:
66
+
67
+ ```python
68
+ server = client.servers.get("srv_abc")
69
+ server.tasks.create("...") # sub-resources are scoped to the server
70
+ server.files.write("app.py", "...")
71
+ server.deploy()
72
+ server.refresh() # re-fetch in place
73
+
74
+ project = client.projects.get("prj_xyz")
75
+ project.update(visibility="public")
76
+ ```
77
+
78
+ `task.wait()` / `build.wait()` poll until a terminal state; both accept
79
+ `poll_interval` and `timeout` (seconds).
80
+
81
+ ## Errors
82
+
83
+ Every failure is a subclass of `Server4AgentError`, so you can catch broadly or
84
+ narrowly:
85
+
86
+ ```python
87
+ from server4agent import NotFoundError, RateLimitError, APIStatusError
88
+
89
+ try:
90
+ client.servers.get("srv_missing")
91
+ except NotFoundError:
92
+ ... # 404
93
+ except RateLimitError as e:
94
+ ... # 429 (already retried; quote e.request_id to support)
95
+ except APIStatusError as e:
96
+ print(e.status, e.code, e.message, e.request_id)
97
+ ```
98
+
99
+ Retryable failures (429, 5xx, network blips) are retried automatically with
100
+ exponential backoff. Tune it per client:
101
+
102
+ ```python
103
+ Server4Agent(timeout=30.0, max_retries=4)
104
+ ```
105
+
106
+ ## Verifying webhooks
107
+
108
+ ```python
109
+ import os
110
+ from server4agent import verify_webhook_signature
111
+
112
+ # request.data must be the raw body — parse it as JSON only after verifying.
113
+ ok = verify_webhook_signature(
114
+ secret=os.environ["SERVER4AGENT_WEBHOOK_SECRET"],
115
+ body=request.data.decode("utf-8"),
116
+ header=request.headers.get("Server4Agent-Signature"),
117
+ )
118
+ if not ok:
119
+ return "invalid signature", 401
120
+ ```
121
+
122
+ ## API surface
123
+
124
+ `servers`, `projects`, `templates`, `tasks`, `builds`, `files`, `keys`,
125
+ `webhooks` — see the [REST API docs](https://www.server4agent.com/docs/api) for
126
+ the endpoints each method wraps.
127
+
128
+ Server-side only: this SDK holds your `sk_live_` key. Never embed it in a
129
+ notebook or script you share.
@@ -0,0 +1,24 @@
1
+ # Security policy
2
+
3
+ ## Reporting a vulnerability
4
+
5
+ Please report security issues **privately** — do not open a public issue or PR.
6
+
7
+ - Use GitHub's private vulnerability reporting on this repo (**Security → Report a
8
+ vulnerability**), or
9
+ - email **security@server4agent.com**.
10
+
11
+ We aim to acknowledge reports within 3 business days.
12
+
13
+ ## Handling your API key
14
+
15
+ This SDK holds an `sk_live_` key and is meant for **server-side** use only.
16
+
17
+ - Never embed it in a shared notebook or script, and never commit it to source
18
+ control. Prefer the `SERVER4AGENT_API_KEY` environment variable.
19
+ - Rotate a key immediately if it may have been exposed (`client.keys.revoke(id)`).
20
+
21
+ ## Supported versions
22
+
23
+ Only the latest published `0.x` release receives security fixes while the SDK
24
+ is pre-1.0.
@@ -0,0 +1,19 @@
1
+ """Give the on-server agent a goal and await the result, using the async client.
2
+
3
+ SERVER4AGENT_API_KEY=sk_live_... python async_quickstart.py
4
+ """
5
+ import asyncio
6
+
7
+ from server4agent import AsyncServer4Agent
8
+
9
+
10
+ async def main() -> None:
11
+ async with AsyncServer4Agent() as client:
12
+ server = await client.servers.create(tier="small")
13
+ task = await server.tasks.create("Summarize today's top HN posts into summary.md")
14
+ await task.wait(on_poll=lambda t: print("task:", t.status))
15
+ print(task.result)
16
+
17
+
18
+ if __name__ == "__main__":
19
+ asyncio.run(main())
@@ -0,0 +1,19 @@
1
+ """Provision a server, have the agent build an app, and print the live URL.
2
+
3
+ SERVER4AGENT_API_KEY=sk_live_... python quickstart.py
4
+ """
5
+ from server4agent import Server4Agent
6
+
7
+
8
+ def main() -> None:
9
+ with Server4Agent() as client:
10
+ server = client.servers.create(tier="small")
11
+ print(f"server {server.id} ({server.status})")
12
+
13
+ build = server.builds.start("a FastAPI todo API with a small web UI")
14
+ build.wait(on_poll=lambda b: print(f" build: {b.status}"))
15
+ print("live at", build.url)
16
+
17
+
18
+ if __name__ == "__main__":
19
+ main()
@@ -0,0 +1,40 @@
1
+ [build-system]
2
+ requires = ["hatchling"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "server4agent"
7
+ description = "Python client for the Server4Agent REST API — sync + async, typed, with retries."
8
+ readme = "README.md"
9
+ requires-python = ">=3.9"
10
+ license = { text = "MIT" }
11
+ keywords = ["server4agent", "sdk", "agents", "mcp"]
12
+ dynamic = ["version"]
13
+ dependencies = [
14
+ "httpx>=0.24,<1",
15
+ ]
16
+ classifiers = [
17
+ "Development Status :: 4 - Beta",
18
+ "Intended Audience :: Developers",
19
+ "License :: OSI Approved :: MIT License",
20
+ "Programming Language :: Python :: 3",
21
+ "Programming Language :: Python :: 3 :: Only",
22
+ "Typing :: Typed",
23
+ ]
24
+
25
+ [project.optional-dependencies]
26
+ dev = ["pytest>=7"]
27
+
28
+ [project.urls]
29
+ Homepage = "https://www.server4agent.com"
30
+ Documentation = "https://www.server4agent.com/docs/sdks"
31
+ Repository = "https://github.com/Server4Agent/server4agent-python"
32
+
33
+ [tool.hatch.version]
34
+ path = "server4agent/_version.py"
35
+
36
+ [tool.hatch.build.targets.wheel]
37
+ packages = ["server4agent"]
38
+
39
+ [tool.pytest.ini_options]
40
+ testpaths = ["tests"]
@@ -0,0 +1,79 @@
1
+ from ._async.client import AsyncServer4Agent
2
+ from ._async.client import (
3
+ AsyncBuild,
4
+ AsyncProject,
5
+ AsyncServer,
6
+ AsyncTask,
7
+ )
8
+ from ._models import (
9
+ ApiKey,
10
+ BuildStep,
11
+ CreatedApiKey,
12
+ CreatedWebhook,
13
+ Deployment,
14
+ ExecResult,
15
+ ProjectTemplate,
16
+ ProjectTemplateDefaults,
17
+ WEBHOOK_EVENTS,
18
+ Webhook,
19
+ )
20
+ from ._sync.client import Build, Project, Server, Server4Agent, Task
21
+ from ._version import __version__
22
+ from .errors import (
23
+ APIConnectionError,
24
+ APIStatusError,
25
+ APITimeoutError,
26
+ AuthenticationError,
27
+ BadRequestError,
28
+ ConflictError,
29
+ InternalServerError,
30
+ NotFoundError,
31
+ PermissionDeniedError,
32
+ RateLimitError,
33
+ Server4AgentError,
34
+ UnprocessableEntityError,
35
+ )
36
+ from .webhooks import verify_webhook_signature
37
+
38
+ __all__ = [
39
+ # clients
40
+ "Server4Agent",
41
+ "AsyncServer4Agent",
42
+ # sync handles
43
+ "Server",
44
+ "Project",
45
+ "Task",
46
+ "Build",
47
+ # async handles
48
+ "AsyncServer",
49
+ "AsyncProject",
50
+ "AsyncTask",
51
+ "AsyncBuild",
52
+ # data types
53
+ "ApiKey",
54
+ "CreatedApiKey",
55
+ "Webhook",
56
+ "CreatedWebhook",
57
+ "Deployment",
58
+ "ExecResult",
59
+ "BuildStep",
60
+ "ProjectTemplate",
61
+ "ProjectTemplateDefaults",
62
+ "WEBHOOK_EVENTS",
63
+ # errors
64
+ "Server4AgentError",
65
+ "APIConnectionError",
66
+ "APITimeoutError",
67
+ "APIStatusError",
68
+ "BadRequestError",
69
+ "AuthenticationError",
70
+ "PermissionDeniedError",
71
+ "NotFoundError",
72
+ "ConflictError",
73
+ "UnprocessableEntityError",
74
+ "RateLimitError",
75
+ "InternalServerError",
76
+ # webhooks
77
+ "verify_webhook_signature",
78
+ "__version__",
79
+ ]
File without changes