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.
Files changed (47) hide show
  1. afd-0.1.0/.gitignore +47 -0
  2. afd-0.1.0/PKG-INFO +200 -0
  3. afd-0.1.0/README.md +154 -0
  4. afd-0.1.0/examples/__init__.py +9 -0
  5. afd-0.1.0/examples/client_usage.py +112 -0
  6. afd-0.1.0/examples/test_example.py +283 -0
  7. afd-0.1.0/pyproject.toml +90 -0
  8. afd-0.1.0/src/afd/__init__.py +134 -0
  9. afd-0.1.0/src/afd/cli/__init__.py +31 -0
  10. afd-0.1.0/src/afd/cli/main.py +447 -0
  11. afd-0.1.0/src/afd/cli/output.py +287 -0
  12. afd-0.1.0/src/afd/core/__init__.py +111 -0
  13. afd-0.1.0/src/afd/core/commands.py +325 -0
  14. afd-0.1.0/src/afd/core/errors.py +347 -0
  15. afd-0.1.0/src/afd/core/handoff.py +434 -0
  16. afd-0.1.0/src/afd/core/metadata.py +307 -0
  17. afd-0.1.0/src/afd/core/pipeline.py +900 -0
  18. afd-0.1.0/src/afd/core/result.py +263 -0
  19. afd-0.1.0/src/afd/py.typed +1 -0
  20. afd-0.1.0/src/afd/server/__init__.py +61 -0
  21. afd-0.1.0/src/afd/server/bootstrap/__init__.py +29 -0
  22. afd-0.1.0/src/afd/server/bootstrap/afd_docs.py +168 -0
  23. afd-0.1.0/src/afd/server/bootstrap/afd_help.py +164 -0
  24. afd-0.1.0/src/afd/server/bootstrap/afd_schema.py +176 -0
  25. afd-0.1.0/src/afd/server/bootstrap/registry.py +52 -0
  26. afd-0.1.0/src/afd/server/decorators.py +242 -0
  27. afd-0.1.0/src/afd/server/factory.py +438 -0
  28. afd-0.1.0/src/afd/testing/__init__.py +52 -0
  29. afd-0.1.0/src/afd/testing/assertions.py +397 -0
  30. afd-0.1.0/src/afd/testing/fixtures.py +257 -0
  31. afd-0.1.0/src/afd/transports/__init__.py +39 -0
  32. afd-0.1.0/src/afd/transports/base.py +149 -0
  33. afd-0.1.0/src/afd/transports/fastmcp.py +182 -0
  34. afd-0.1.0/src/afd/transports/mock.py +250 -0
  35. afd-0.1.0/tests/__init__.py +1 -0
  36. afd-0.1.0/tests/conftest.py +3 -0
  37. afd-0.1.0/tests/test_bootstrap.py +503 -0
  38. afd-0.1.0/tests/test_cli.py +349 -0
  39. afd-0.1.0/tests/test_commands.py +329 -0
  40. afd-0.1.0/tests/test_errors.py +241 -0
  41. afd-0.1.0/tests/test_handoff.py +471 -0
  42. afd-0.1.0/tests/test_metadata.py +293 -0
  43. afd-0.1.0/tests/test_pipeline.py +805 -0
  44. afd-0.1.0/tests/test_result.py +228 -0
  45. afd-0.1.0/tests/test_server.py +357 -0
  46. afd-0.1.0/tests/test_testing.py +381 -0
  47. 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())