afd 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.
- afd-0.1.0/.gitignore +47 -0
- afd-0.1.0/PKG-INFO +200 -0
- afd-0.1.0/README.md +154 -0
- afd-0.1.0/examples/__init__.py +9 -0
- afd-0.1.0/examples/client_usage.py +112 -0
- afd-0.1.0/examples/test_example.py +283 -0
- afd-0.1.0/pyproject.toml +90 -0
- afd-0.1.0/src/afd/__init__.py +134 -0
- afd-0.1.0/src/afd/cli/__init__.py +31 -0
- afd-0.1.0/src/afd/cli/main.py +447 -0
- afd-0.1.0/src/afd/cli/output.py +287 -0
- afd-0.1.0/src/afd/core/__init__.py +111 -0
- afd-0.1.0/src/afd/core/commands.py +325 -0
- afd-0.1.0/src/afd/core/errors.py +347 -0
- afd-0.1.0/src/afd/core/handoff.py +434 -0
- afd-0.1.0/src/afd/core/metadata.py +307 -0
- afd-0.1.0/src/afd/core/pipeline.py +900 -0
- afd-0.1.0/src/afd/core/result.py +263 -0
- afd-0.1.0/src/afd/py.typed +1 -0
- afd-0.1.0/src/afd/server/__init__.py +61 -0
- afd-0.1.0/src/afd/server/bootstrap/__init__.py +29 -0
- afd-0.1.0/src/afd/server/bootstrap/afd_docs.py +168 -0
- afd-0.1.0/src/afd/server/bootstrap/afd_help.py +164 -0
- afd-0.1.0/src/afd/server/bootstrap/afd_schema.py +176 -0
- afd-0.1.0/src/afd/server/bootstrap/registry.py +52 -0
- afd-0.1.0/src/afd/server/decorators.py +242 -0
- afd-0.1.0/src/afd/server/factory.py +438 -0
- afd-0.1.0/src/afd/testing/__init__.py +52 -0
- afd-0.1.0/src/afd/testing/assertions.py +397 -0
- afd-0.1.0/src/afd/testing/fixtures.py +257 -0
- afd-0.1.0/src/afd/transports/__init__.py +39 -0
- afd-0.1.0/src/afd/transports/base.py +149 -0
- afd-0.1.0/src/afd/transports/fastmcp.py +182 -0
- afd-0.1.0/src/afd/transports/mock.py +250 -0
- afd-0.1.0/tests/__init__.py +1 -0
- afd-0.1.0/tests/conftest.py +3 -0
- afd-0.1.0/tests/test_bootstrap.py +503 -0
- afd-0.1.0/tests/test_cli.py +349 -0
- afd-0.1.0/tests/test_commands.py +329 -0
- afd-0.1.0/tests/test_errors.py +241 -0
- afd-0.1.0/tests/test_handoff.py +471 -0
- afd-0.1.0/tests/test_metadata.py +293 -0
- afd-0.1.0/tests/test_pipeline.py +805 -0
- afd-0.1.0/tests/test_result.py +228 -0
- afd-0.1.0/tests/test_server.py +357 -0
- afd-0.1.0/tests/test_testing.py +381 -0
- afd-0.1.0/tests/test_transports.py +260 -0
afd-0.1.0/.gitignore
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# Dependencies
|
|
2
|
+
node_modules/
|
|
3
|
+
.pnpm-store/
|
|
4
|
+
package-lock.json
|
|
5
|
+
|
|
6
|
+
# Build outputs
|
|
7
|
+
dist/
|
|
8
|
+
*.tsbuildinfo
|
|
9
|
+
|
|
10
|
+
# Python
|
|
11
|
+
__pycache__/
|
|
12
|
+
*.py[cod]
|
|
13
|
+
*$py.class
|
|
14
|
+
*.so
|
|
15
|
+
.Python
|
|
16
|
+
.venv/
|
|
17
|
+
venv/
|
|
18
|
+
.eggs/
|
|
19
|
+
*.egg-info/
|
|
20
|
+
*.egg
|
|
21
|
+
|
|
22
|
+
# IDE
|
|
23
|
+
.idea/
|
|
24
|
+
.vscode/
|
|
25
|
+
*.swp
|
|
26
|
+
*.swo
|
|
27
|
+
.DS_Store
|
|
28
|
+
|
|
29
|
+
# Logs
|
|
30
|
+
*.log
|
|
31
|
+
npm-debug.log*
|
|
32
|
+
pnpm-debug.log*
|
|
33
|
+
|
|
34
|
+
# Test coverage
|
|
35
|
+
coverage/
|
|
36
|
+
|
|
37
|
+
# Environment
|
|
38
|
+
.env
|
|
39
|
+
.env.local
|
|
40
|
+
.env.*.local
|
|
41
|
+
|
|
42
|
+
# Misc
|
|
43
|
+
*.plan.md
|
|
44
|
+
!PLAN/**/*.plan.md
|
|
45
|
+
!archive/**/*.plan.md
|
|
46
|
+
.cursor/
|
|
47
|
+
nul
|
afd-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: afd
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Agent-First Development toolkit for Python
|
|
5
|
+
Project-URL: Homepage, https://github.com/Falkicon/afd
|
|
6
|
+
Project-URL: Documentation, https://github.com/Falkicon/afd/tree/main/python
|
|
7
|
+
Project-URL: Repository, https://github.com/Falkicon/afd
|
|
8
|
+
Author-email: Falkicon <falkicon@example.com>
|
|
9
|
+
License-Expression: MIT
|
|
10
|
+
Keywords: afd,agent,agent-first,ai,cli,commands,mcp
|
|
11
|
+
Classifier: Development Status :: 3 - Alpha
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
18
|
+
Classifier: Typing :: Typed
|
|
19
|
+
Requires-Python: >=3.10
|
|
20
|
+
Requires-Dist: pydantic>=2.0
|
|
21
|
+
Provides-Extra: all
|
|
22
|
+
Requires-Dist: click>=8.0; extra == 'all'
|
|
23
|
+
Requires-Dist: fastmcp>=0.1.0; extra == 'all'
|
|
24
|
+
Requires-Dist: pytest-asyncio>=0.23; extra == 'all'
|
|
25
|
+
Requires-Dist: pytest>=8.0; extra == 'all'
|
|
26
|
+
Requires-Dist: rich>=13.0; extra == 'all'
|
|
27
|
+
Provides-Extra: cli
|
|
28
|
+
Requires-Dist: click>=8.0; extra == 'cli'
|
|
29
|
+
Requires-Dist: fastmcp>=0.1.0; extra == 'cli'
|
|
30
|
+
Requires-Dist: rich>=13.0; extra == 'cli'
|
|
31
|
+
Provides-Extra: dev
|
|
32
|
+
Requires-Dist: click>=8.0; extra == 'dev'
|
|
33
|
+
Requires-Dist: fastmcp>=0.1.0; extra == 'dev'
|
|
34
|
+
Requires-Dist: mypy>=1.0; extra == 'dev'
|
|
35
|
+
Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
|
|
36
|
+
Requires-Dist: pytest-cov>=4.0; extra == 'dev'
|
|
37
|
+
Requires-Dist: pytest>=8.0; extra == 'dev'
|
|
38
|
+
Requires-Dist: rich>=13.0; extra == 'dev'
|
|
39
|
+
Requires-Dist: ruff>=0.1.0; extra == 'dev'
|
|
40
|
+
Provides-Extra: server
|
|
41
|
+
Requires-Dist: fastmcp>=0.1.0; extra == 'server'
|
|
42
|
+
Provides-Extra: testing
|
|
43
|
+
Requires-Dist: pytest-asyncio>=0.23; extra == 'testing'
|
|
44
|
+
Requires-Dist: pytest>=8.0; extra == 'testing'
|
|
45
|
+
Description-Content-Type: text/markdown
|
|
46
|
+
|
|
47
|
+
# AFD - Agent-First Development for Python
|
|
48
|
+
|
|
49
|
+
A Python toolkit for building applications with the Agent-First Development methodology.
|
|
50
|
+
|
|
51
|
+
## What is AFD?
|
|
52
|
+
|
|
53
|
+
**Agent-First Development** is a software development methodology where AI agents are treated as first-class users from day one. Instead of building UI first and adding API/agent access later, AFD inverts this:
|
|
54
|
+
|
|
55
|
+
```
|
|
56
|
+
Traditional: UI → API → Agent Access (afterthought)
|
|
57
|
+
Agent-First: Commands → Validation → UI (surface)
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
## Installation
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
# Core types only
|
|
64
|
+
pip install afd
|
|
65
|
+
|
|
66
|
+
# With MCP server support
|
|
67
|
+
pip install afd[server]
|
|
68
|
+
|
|
69
|
+
# With CLI
|
|
70
|
+
pip install afd[cli]
|
|
71
|
+
|
|
72
|
+
# With testing utilities
|
|
73
|
+
pip install afd[testing]
|
|
74
|
+
|
|
75
|
+
# Everything
|
|
76
|
+
pip install afd[all]
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## Quick Start
|
|
80
|
+
|
|
81
|
+
### Define a Command
|
|
82
|
+
|
|
83
|
+
```python
|
|
84
|
+
from afd import CommandResult, success, error
|
|
85
|
+
from afd.server import define_command
|
|
86
|
+
from pydantic import BaseModel
|
|
87
|
+
|
|
88
|
+
class Todo(BaseModel):
|
|
89
|
+
id: str
|
|
90
|
+
title: str
|
|
91
|
+
done: bool = False
|
|
92
|
+
|
|
93
|
+
@define_command(
|
|
94
|
+
name="todo.create",
|
|
95
|
+
description="Create a new todo item",
|
|
96
|
+
)
|
|
97
|
+
async def create_todo(title: str) -> CommandResult[Todo]:
|
|
98
|
+
todo = Todo(id="todo-1", title=title)
|
|
99
|
+
return success(
|
|
100
|
+
data=todo,
|
|
101
|
+
reasoning="Created new todo with default status",
|
|
102
|
+
)
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
### Create an MCP Server
|
|
106
|
+
|
|
107
|
+
```python
|
|
108
|
+
from afd.server import create_server
|
|
109
|
+
|
|
110
|
+
server = create_server(
|
|
111
|
+
name="todo-app",
|
|
112
|
+
version="1.0.0",
|
|
113
|
+
)
|
|
114
|
+
|
|
115
|
+
@server.command(
|
|
116
|
+
name="todo.create",
|
|
117
|
+
description="Create a todo",
|
|
118
|
+
)
|
|
119
|
+
async def create_todo(input):
|
|
120
|
+
todo = Todo(id="todo-1", title=input["title"])
|
|
121
|
+
return success(data=todo)
|
|
122
|
+
|
|
123
|
+
# Run the server (stdio for VS Code/Cursor)
|
|
124
|
+
server.run()
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
### Test Your Commands
|
|
128
|
+
|
|
129
|
+
```python
|
|
130
|
+
import pytest
|
|
131
|
+
from afd.testing import assert_success
|
|
132
|
+
|
|
133
|
+
# Use the mock_server fixture
|
|
134
|
+
async def test_create_todo(mock_server):
|
|
135
|
+
@mock_server.command("todo.create")
|
|
136
|
+
async def handler(input):
|
|
137
|
+
from afd import success
|
|
138
|
+
return success({"id": "1", "title": input["title"]})
|
|
139
|
+
|
|
140
|
+
result = await mock_server.execute("todo.create", {"title": "Test"})
|
|
141
|
+
|
|
142
|
+
data = assert_success(result)
|
|
143
|
+
assert data["title"] == "Test"
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
## Core Types
|
|
147
|
+
|
|
148
|
+
### CommandResult
|
|
149
|
+
|
|
150
|
+
The standard return type for all commands:
|
|
151
|
+
|
|
152
|
+
```python
|
|
153
|
+
from afd import CommandResult, success, error
|
|
154
|
+
|
|
155
|
+
# Successful result
|
|
156
|
+
result = success(
|
|
157
|
+
data={"id": "123"},
|
|
158
|
+
reasoning="Created successfully",
|
|
159
|
+
confidence=0.95,
|
|
160
|
+
)
|
|
161
|
+
|
|
162
|
+
# Error result
|
|
163
|
+
result = error(
|
|
164
|
+
code="NOT_FOUND",
|
|
165
|
+
message="Resource not found",
|
|
166
|
+
suggestion="Check the ID and try again",
|
|
167
|
+
)
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
### UX-Enabling Fields
|
|
171
|
+
|
|
172
|
+
AFD results include optional fields that enable rich agent experiences:
|
|
173
|
+
|
|
174
|
+
| Field | Purpose |
|
|
175
|
+
| -------------- | -------------------------------------- |
|
|
176
|
+
| `confidence` | 0-1 score for UI confidence indicators |
|
|
177
|
+
| `reasoning` | Explains "why" for transparency |
|
|
178
|
+
| `sources` | Attribution for verification |
|
|
179
|
+
| `plan` | Multi-step operation visibility |
|
|
180
|
+
| `alternatives` | Other options considered |
|
|
181
|
+
| `warnings` | Non-fatal issues to surface |
|
|
182
|
+
|
|
183
|
+
## Packages
|
|
184
|
+
|
|
185
|
+
| Extra | Contents |
|
|
186
|
+
| ----------- | -------------------------------------------------------------------- |
|
|
187
|
+
| (core) | `CommandResult`, `success()`, `error()`, error types, metadata types |
|
|
188
|
+
| `[server]` | MCP server factory, `@define_command`, `create_server()` |
|
|
189
|
+
| `[cli]` | Click-based CLI for connecting to MCP servers |
|
|
190
|
+
| `[testing]` | `mock_server` fixture, assertions, `MockTransport` |
|
|
191
|
+
|
|
192
|
+
## Related
|
|
193
|
+
|
|
194
|
+
- [AFD TypeScript](../packages/) - Original TypeScript implementation
|
|
195
|
+
- [AFD Philosophy](../docs/philosophy.md) - Why AFD?
|
|
196
|
+
- [Command Schema Guide](../docs/command-schema-guide.md) - Designing commands
|
|
197
|
+
|
|
198
|
+
## License
|
|
199
|
+
|
|
200
|
+
MIT
|
afd-0.1.0/README.md
ADDED
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
# AFD - Agent-First Development for Python
|
|
2
|
+
|
|
3
|
+
A Python toolkit for building applications with the Agent-First Development methodology.
|
|
4
|
+
|
|
5
|
+
## What is AFD?
|
|
6
|
+
|
|
7
|
+
**Agent-First Development** is a software development methodology where AI agents are treated as first-class users from day one. Instead of building UI first and adding API/agent access later, AFD inverts this:
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
Traditional: UI → API → Agent Access (afterthought)
|
|
11
|
+
Agent-First: Commands → Validation → UI (surface)
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
## Installation
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
# Core types only
|
|
18
|
+
pip install afd
|
|
19
|
+
|
|
20
|
+
# With MCP server support
|
|
21
|
+
pip install afd[server]
|
|
22
|
+
|
|
23
|
+
# With CLI
|
|
24
|
+
pip install afd[cli]
|
|
25
|
+
|
|
26
|
+
# With testing utilities
|
|
27
|
+
pip install afd[testing]
|
|
28
|
+
|
|
29
|
+
# Everything
|
|
30
|
+
pip install afd[all]
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Quick Start
|
|
34
|
+
|
|
35
|
+
### Define a Command
|
|
36
|
+
|
|
37
|
+
```python
|
|
38
|
+
from afd import CommandResult, success, error
|
|
39
|
+
from afd.server import define_command
|
|
40
|
+
from pydantic import BaseModel
|
|
41
|
+
|
|
42
|
+
class Todo(BaseModel):
|
|
43
|
+
id: str
|
|
44
|
+
title: str
|
|
45
|
+
done: bool = False
|
|
46
|
+
|
|
47
|
+
@define_command(
|
|
48
|
+
name="todo.create",
|
|
49
|
+
description="Create a new todo item",
|
|
50
|
+
)
|
|
51
|
+
async def create_todo(title: str) -> CommandResult[Todo]:
|
|
52
|
+
todo = Todo(id="todo-1", title=title)
|
|
53
|
+
return success(
|
|
54
|
+
data=todo,
|
|
55
|
+
reasoning="Created new todo with default status",
|
|
56
|
+
)
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
### Create an MCP Server
|
|
60
|
+
|
|
61
|
+
```python
|
|
62
|
+
from afd.server import create_server
|
|
63
|
+
|
|
64
|
+
server = create_server(
|
|
65
|
+
name="todo-app",
|
|
66
|
+
version="1.0.0",
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
@server.command(
|
|
70
|
+
name="todo.create",
|
|
71
|
+
description="Create a todo",
|
|
72
|
+
)
|
|
73
|
+
async def create_todo(input):
|
|
74
|
+
todo = Todo(id="todo-1", title=input["title"])
|
|
75
|
+
return success(data=todo)
|
|
76
|
+
|
|
77
|
+
# Run the server (stdio for VS Code/Cursor)
|
|
78
|
+
server.run()
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
### Test Your Commands
|
|
82
|
+
|
|
83
|
+
```python
|
|
84
|
+
import pytest
|
|
85
|
+
from afd.testing import assert_success
|
|
86
|
+
|
|
87
|
+
# Use the mock_server fixture
|
|
88
|
+
async def test_create_todo(mock_server):
|
|
89
|
+
@mock_server.command("todo.create")
|
|
90
|
+
async def handler(input):
|
|
91
|
+
from afd import success
|
|
92
|
+
return success({"id": "1", "title": input["title"]})
|
|
93
|
+
|
|
94
|
+
result = await mock_server.execute("todo.create", {"title": "Test"})
|
|
95
|
+
|
|
96
|
+
data = assert_success(result)
|
|
97
|
+
assert data["title"] == "Test"
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
## Core Types
|
|
101
|
+
|
|
102
|
+
### CommandResult
|
|
103
|
+
|
|
104
|
+
The standard return type for all commands:
|
|
105
|
+
|
|
106
|
+
```python
|
|
107
|
+
from afd import CommandResult, success, error
|
|
108
|
+
|
|
109
|
+
# Successful result
|
|
110
|
+
result = success(
|
|
111
|
+
data={"id": "123"},
|
|
112
|
+
reasoning="Created successfully",
|
|
113
|
+
confidence=0.95,
|
|
114
|
+
)
|
|
115
|
+
|
|
116
|
+
# Error result
|
|
117
|
+
result = error(
|
|
118
|
+
code="NOT_FOUND",
|
|
119
|
+
message="Resource not found",
|
|
120
|
+
suggestion="Check the ID and try again",
|
|
121
|
+
)
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
### UX-Enabling Fields
|
|
125
|
+
|
|
126
|
+
AFD results include optional fields that enable rich agent experiences:
|
|
127
|
+
|
|
128
|
+
| Field | Purpose |
|
|
129
|
+
| -------------- | -------------------------------------- |
|
|
130
|
+
| `confidence` | 0-1 score for UI confidence indicators |
|
|
131
|
+
| `reasoning` | Explains "why" for transparency |
|
|
132
|
+
| `sources` | Attribution for verification |
|
|
133
|
+
| `plan` | Multi-step operation visibility |
|
|
134
|
+
| `alternatives` | Other options considered |
|
|
135
|
+
| `warnings` | Non-fatal issues to surface |
|
|
136
|
+
|
|
137
|
+
## Packages
|
|
138
|
+
|
|
139
|
+
| Extra | Contents |
|
|
140
|
+
| ----------- | -------------------------------------------------------------------- |
|
|
141
|
+
| (core) | `CommandResult`, `success()`, `error()`, error types, metadata types |
|
|
142
|
+
| `[server]` | MCP server factory, `@define_command`, `create_server()` |
|
|
143
|
+
| `[cli]` | Click-based CLI for connecting to MCP servers |
|
|
144
|
+
| `[testing]` | `mock_server` fixture, assertions, `MockTransport` |
|
|
145
|
+
|
|
146
|
+
## Related
|
|
147
|
+
|
|
148
|
+
- [AFD TypeScript](../packages/) - Original TypeScript implementation
|
|
149
|
+
- [AFD Philosophy](../docs/philosophy.md) - Why AFD?
|
|
150
|
+
- [Command Schema Guide](../docs/command-schema-guide.md) - Designing commands
|
|
151
|
+
|
|
152
|
+
## License
|
|
153
|
+
|
|
154
|
+
MIT
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
"""AFD Examples.
|
|
2
|
+
|
|
3
|
+
This package contains example code demonstrating how to use AFD.
|
|
4
|
+
|
|
5
|
+
Examples:
|
|
6
|
+
- todo_server.py: A complete MCP server for managing todos
|
|
7
|
+
- test_example.py: Testing patterns with AFD assertions
|
|
8
|
+
- client_usage.py: Using the transport layer programmatically
|
|
9
|
+
"""
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
"""Example: Using the AFD CLI.
|
|
2
|
+
|
|
3
|
+
This script demonstrates how to use the AFD client library
|
|
4
|
+
to connect to and interact with MCP servers programmatically.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import asyncio
|
|
8
|
+
from afd.transports import MockTransport, TransportConfig
|
|
9
|
+
from afd import success
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
async def main():
|
|
13
|
+
"""Demonstrate AFD client usage."""
|
|
14
|
+
|
|
15
|
+
# ==============================================================================
|
|
16
|
+
# Create a MockTransport for demonstration
|
|
17
|
+
# ==============================================================================
|
|
18
|
+
|
|
19
|
+
transport = MockTransport(
|
|
20
|
+
config=TransportConfig(
|
|
21
|
+
timeout_ms=5000,
|
|
22
|
+
)
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
# Register some mock tools
|
|
26
|
+
async def echo(args):
|
|
27
|
+
return success({"message": args.get("text", "Hello!")})
|
|
28
|
+
|
|
29
|
+
async def add_numbers(args):
|
|
30
|
+
a = args.get("a", 0)
|
|
31
|
+
b = args.get("b", 0)
|
|
32
|
+
return success({"result": a + b})
|
|
33
|
+
|
|
34
|
+
transport.register_tool(
|
|
35
|
+
name="echo",
|
|
36
|
+
handler=echo,
|
|
37
|
+
description="Echo back a message",
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
transport.register_tool(
|
|
41
|
+
name="math.add",
|
|
42
|
+
handler=add_numbers,
|
|
43
|
+
description="Add two numbers",
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
# ==============================================================================
|
|
47
|
+
# Connect to the server
|
|
48
|
+
# ==============================================================================
|
|
49
|
+
|
|
50
|
+
print("Connecting to server...")
|
|
51
|
+
await transport.connect()
|
|
52
|
+
print(f" State: {transport.state.value}")
|
|
53
|
+
print()
|
|
54
|
+
|
|
55
|
+
# ==============================================================================
|
|
56
|
+
# List available tools
|
|
57
|
+
# ==============================================================================
|
|
58
|
+
|
|
59
|
+
print("Available tools:")
|
|
60
|
+
tools = await transport.list_tools()
|
|
61
|
+
for tool in tools:
|
|
62
|
+
print(f" - {tool.name}: {tool.description}")
|
|
63
|
+
print()
|
|
64
|
+
|
|
65
|
+
# ==============================================================================
|
|
66
|
+
# Call tools
|
|
67
|
+
# ==============================================================================
|
|
68
|
+
|
|
69
|
+
print("Calling 'echo' tool...")
|
|
70
|
+
result = await transport.call_tool("echo", {"text": "Hello, AFD!"})
|
|
71
|
+
print(f" Success: {result.success}")
|
|
72
|
+
print(f" Data: {result.data}")
|
|
73
|
+
print()
|
|
74
|
+
|
|
75
|
+
print("Calling 'math.add' tool...")
|
|
76
|
+
result = await transport.call_tool("math.add", {"a": 42, "b": 58})
|
|
77
|
+
print(f" Success: {result.success}")
|
|
78
|
+
print(f" Data: {result.data}")
|
|
79
|
+
print()
|
|
80
|
+
|
|
81
|
+
# ==============================================================================
|
|
82
|
+
# Call a non-existent tool (demonstrates error handling)
|
|
83
|
+
# ==============================================================================
|
|
84
|
+
|
|
85
|
+
print("Calling non-existent tool...")
|
|
86
|
+
try:
|
|
87
|
+
result = await transport.call_tool("does.not.exist", {})
|
|
88
|
+
print(f" Success: {result.success}")
|
|
89
|
+
except Exception as e:
|
|
90
|
+
print(f" Error (expected): {type(e).__name__}: {e}")
|
|
91
|
+
print()
|
|
92
|
+
|
|
93
|
+
# ==============================================================================
|
|
94
|
+
# View call history (MockTransport feature)
|
|
95
|
+
# ==============================================================================
|
|
96
|
+
|
|
97
|
+
print("Call history:")
|
|
98
|
+
for call in transport.calls:
|
|
99
|
+
print(f" {call.tool_name}: {call.arguments}")
|
|
100
|
+
print()
|
|
101
|
+
|
|
102
|
+
# ==============================================================================
|
|
103
|
+
# Disconnect
|
|
104
|
+
# ==============================================================================
|
|
105
|
+
|
|
106
|
+
print("Disconnecting...")
|
|
107
|
+
await transport.disconnect()
|
|
108
|
+
print(f" State: {transport.state.value}")
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
if __name__ == "__main__":
|
|
112
|
+
asyncio.run(main())
|