# ftl-sdk (Python)
Python SDK for building Model Context Protocol (MCP) tools that compile to WebAssembly.
[](https://pypi.org/project/ftl-sdk/)
[](https://pypi.org/project/ftl-sdk/)
[](https://github.com/fastertools/ftl-cli/blob/main/LICENSE)
[](https://github.com/fastertools/ftl-cli/actions)
## Installation
### Latest Stable Version
```bash
pip install ftl-sdk
```
### Specific Version
```bash
pip install ftl-sdk==0.1.0
```
### Development Version
```bash
pip install git+https://github.com/fastertools/ftl-cli.git#subdirectory=sdk/python
```
### From TestPyPI (Pre-releases)
```bash
pip install --index-url https://test.pypi.org/simple/ --extra-index-url https://pypi.org/simple/ ftl-sdk
```
## Overview
This SDK provides:
- Decorator-based API for easy tool creation
- Automatic JSON Schema generation from type hints
- Support for both sync and async functions
- Automatic return value conversion to MCP format
- Zero-dependency implementation (only requires `spin-sdk`)
- Full compatibility with Spin WebAssembly components
- Seamless deployment to Fermyon Cloud
## Requirements
- Python 3.10 or later
- `componentize-py` for building WebAssembly components
- `spin-sdk` for Spin runtime integration
## Quick Start
### 1. Create a new Python tool
```python
from ftl_sdk import FTL
# Create FTL application instance
ftl = FTL()
@ftl.tool
def echo(message: str) -> str:
"""Echo back the input."""
return f"Echo: {message}"
# Create the Spin handler
Handler = ftl.create_handler()
```
### 3. Build and Deploy
```bash
# Create virtual environment
python3 -m venv venv
source venv/bin/activate
# Install dependencies
pip install componentize-py spin-sdk ftl-sdk
# Build
ftl build
# Deploy to Fermyon Cloud
ftl deploy
```
## API Reference
### Decorator-based API
#### `FTL` Class
```python
ftl = FTL()
```
Creates an FTL application instance for registering tools.
#### `@ftl.tool` Decorator
```python
@ftl.tool
def my_tool(param: str) -> str:
"""Tool description."""
return result
# With custom name and annotations
@ftl.tool(name="custom_name", annotations={"priority": "high"})
def another_tool(data: dict) -> dict:
return {"processed": data}
```
The decorator:
- Automatically generates JSON Schema from type hints
- Extracts docstring as tool description
- Handles both sync and async functions
- Validates output against return type annotation
#### `ftl.create_handler()`
Creates a Spin HTTP handler that:
- Returns tool metadata on GET / requests
- Routes to specific tools on POST /{tool_name} requests
- Handles async/await for async tool functions
- Automatically converts return values to MCP format
### `ToolResponse` Helper Methods
```python
# Simple text response
ToolResponse.text("Hello, world!")
# Error response
ToolResponse.error("Something went wrong")
# Response with structured content
ToolResponse.with_structured("Operation complete", {"result": 42})
```
### `ToolContent` Helper Methods
```python
# Text content
ToolContent.text("Some text", {"priority": 0.8})
# Image content
ToolContent.image(base64_data, "image/png")
# Audio content
ToolContent.audio(base64_data, "audio/wav")
# Resource reference
ToolContent.resource({"uri": "file:///example.txt"})
```
### Type Guards
```python
# Check content types
if is_text_content(content):
print(content["text"])
```
## Examples
### Basic Tools
```python
from ftl_sdk import FTL
ftl = FTL()
@ftl.tool
def echo(message: str) -> str:
"""Echo the input."""
return f"Echo: {message}"
@ftl.tool
def reverse_text(text: str) -> str:
"""Reverse the input text."""
return text[::-1]
@ftl.tool
def word_count(text: str) -> dict:
"""Count words in text."""
count = len(text.split())
return {"text": text, "word_count": count}
Handler = ftl.create_handler()
```
### Async Tools
```python
import asyncio
from ftl_sdk import FTL
ftl = FTL()
@ftl.tool
async def fetch_data(url: str) -> dict:
"""Fetch data from URL asynchronously."""
# Simulate async HTTP request
await asyncio.sleep(0.1)
return {
"url": url,
"status": "success",
"data": {"example": "data"}
}
@ftl.tool
async def process_items(items: list[str]) -> dict:
"""Process items with async operations."""
results = []
for item in items:
# Simulate async processing
await asyncio.sleep(0.01)
results.append(item.upper())
return {
"original": items,
"processed": results,
"count": len(results)
}
# Mix sync and async tools
@ftl.tool
def sync_add(a: int, b: int) -> int:
"""Add two numbers synchronously."""
return a + b
Handler = ftl.create_handler()
```
### Error Handling
```python
from ftl_sdk import FTL
ftl = FTL()
@ftl.tool
def divide(a: float, b: float) -> float:
"""Divide two numbers."""
if b == 0:
raise ValueError("Cannot divide by zero")
return a / b
# Async error handling
@ftl.tool
async def validate_data(data: dict) -> dict:
"""Validate data asynchronously."""
if "required_field" not in data:
raise KeyError("Missing required field: required_field")
# Simulate async validation
await asyncio.sleep(0.05)
if not isinstance(data["required_field"], str):
raise TypeError("required_field must be a string")
return {"status": "valid", "data": data}
Handler = ftl.create_handler()
```
The FTL framework automatically catches exceptions and returns them as error responses.
## Development
### Setting Up Development Environment
1. **Clone the repository**:
```bash
git clone https://github.com/fastertools/ftl-cli.git
cd ftl-cli/sdk/python
```
2. **Create virtual environment**:
```bash
python -m venv venv
source venv/bin/activate # On Windows: venv\Scripts\activate
```
3. **Install development dependencies**:
```bash
make install-dev
# or manually:
pip install -e ".[dev]"
pip install componentize-py
```
### Running Tests
```bash
# Run all tests
make test
# Run tests with coverage
make test-cov
# Run specific test
pytest tests/test_ftl_sdk.py::test_tool_response_text
```
### Code Quality
```bash
# Format code
make format
# Run linting
make lint
# Type checking
make type-check
# Run all quality checks
make quality
```
### Available Make Commands
```bash
make help # Show all available commands
make install # Install SDK
make install-dev # Install with dev dependencies
make format # Format code with black
make lint # Run linting with ruff
make type-check # Run type checking with mypy
make test # Run tests
make test-cov # Run tests with coverage
make clean # Clean build artifacts
make build # Build distribution packages
make publish # Publish to PyPI
```
## Building to WebAssembly
Tools must be compiled to WebAssembly to run on Spin:
1. **Install dependencies**:
```bash
pip install componentize-py spin-sdk ftl-sdk
```
2. **Build with componentize-py**:
```bash
componentize-py -w spin-http componentize app -o app.wasm
```
3. **Or use FTL CLIs build command**:
```bash
ftl build
```
## Best Practices
### Type Hints
Always use type hints for better code clarity and IDE support:
```python
from typing import Dict, Any
from ftl_sdk import ToolResponse
def my_handler(input_data: Dict[str, Any]) -> Dict[str, Any]:
message: str = input_data.get("message", "")
return ToolResponse.text(f"Received: {message}")
```
### Error Handling
Handle errors gracefully and return informative error messages:
```python
def safe_handler(input_data: Dict[str, Any]) -> Dict[str, Any]:
try:
# Validate required fields
if "required_field" not in input_data:
return ToolResponse.error("Missing required field: required_field")
# Process input
result = process_data(input_data["required_field"])
return ToolResponse.text(f"Success: {result}")
except ValueError as e:
return ToolResponse.error(f"Invalid value: {e}")
except Exception as e:
return ToolResponse.error(f"Unexpected error: {e}")
```
### Testing Your Tools
Write comprehensive tests for your tools:
```python
import pytest
from your_module import your_handler
def test_handler_success():
result = your_handler({"message": "test"})
assert result["content"][0]["text"] == "Expected output"
def test_handler_missing_field():
result = your_handler({})
assert result.get("isError") is True
assert "Missing required field" in result["content"][0]["text"]
```
## Important Notes
1. **Python Version**: Requires Python 3.10 or later. Python 3.11+ recommended.
2. **Zero Dependencies**: This SDK has no external dependencies beyond `spin-sdk`, keeping the WASM bundle size minimal.
3. **Input Validation**: The FTL gateway handles input validation against your JSON Schema. Your handler can assume inputs are valid.
4. **Virtual Environments**: Always use a virtual environment to ensure consistent builds.
5. **WASM Size**: Python WASM components are larger than TypeScript/Rust equivalents (~37MB), but this is acceptable for cloud deployment.
6. **Type Safety**: Use type hints and mypy for better code quality and fewer runtime errors.
7. **Code Quality**: The SDK includes development tools (black, ruff, mypy, pytest) to maintain high code quality standards.
## Deployment
Deploy to FTL:
```bash
# Deploy with auto-generated name
ftl deploy
```
## Development
Contributions are welcome! Please feel free to submit a Pull Request.
### Setting Up Development Environment
```bash
# Clone the repository
git clone https://github.com/fastertools/ftl-cli.git
cd ftl-cli/sdk/python
# Create virtual environment
python -m venv venv
source venv/bin/activate # On Windows: venv\Scripts\activate
# Install development dependencies
pip install -e ".[dev]"
pip install tox
```
### Running Tests
```bash
# Run tests for all Python versions
tox
# Run tests for specific Python version
tox -e py311
# Run linting and type checking
tox -e lint,type
# Run tests with coverage
tox -e py311 -- --cov-report=html
```
### Code Quality
```bash
# Format code
black src tests
# Run linter
ruff check src tests
# Type checking
mypy src
```
## Changelog
See [CHANGELOG.md](CHANGELOG.md) for a list of changes in each release.
## License
Apache-2.0 - see [LICENSE](../../LICENSE) for details.
Raw data
{
"_id": null,
"home_page": null,
"name": "ftl-sdk",
"maintainer": null,
"docs_url": null,
"requires_python": ">=3.10",
"maintainer_email": "FTL CLI Contributors <ftl-cli@fastertools.com>",
"keywords": "ftl, mcp, wasm, webassembly, spin, fermyon, model-context-protocol",
"author": null,
"author_email": "FTL CLI Contributors <ftl-cli@fastertools.com>",
"download_url": "https://files.pythonhosted.org/packages/a9/38/de3cf931134c01d5dc4ff34890503faf2ad3d9c393a3fb82453dceca2f99/ftl_sdk-0.1.0.tar.gz",
"platform": null,
"description": "# ftl-sdk (Python)\n\nPython SDK for building Model Context Protocol (MCP) tools that compile to WebAssembly.\n\n[](https://pypi.org/project/ftl-sdk/)\n[](https://pypi.org/project/ftl-sdk/)\n[](https://github.com/fastertools/ftl-cli/blob/main/LICENSE)\n[](https://github.com/fastertools/ftl-cli/actions)\n\n## Installation\n\n### Latest Stable Version\n\n```bash\npip install ftl-sdk\n```\n\n### Specific Version\n\n```bash\npip install ftl-sdk==0.1.0\n```\n\n### Development Version\n\n```bash\npip install git+https://github.com/fastertools/ftl-cli.git#subdirectory=sdk/python\n```\n\n### From TestPyPI (Pre-releases)\n\n```bash\npip install --index-url https://test.pypi.org/simple/ --extra-index-url https://pypi.org/simple/ ftl-sdk\n```\n\n## Overview\n\nThis SDK provides:\n- Decorator-based API for easy tool creation\n- Automatic JSON Schema generation from type hints\n- Support for both sync and async functions\n- Automatic return value conversion to MCP format\n- Zero-dependency implementation (only requires `spin-sdk`)\n- Full compatibility with Spin WebAssembly components\n- Seamless deployment to Fermyon Cloud\n\n## Requirements\n\n- Python 3.10 or later\n- `componentize-py` for building WebAssembly components\n- `spin-sdk` for Spin runtime integration\n\n## Quick Start\n\n### 1. Create a new Python tool\n\n```python\nfrom ftl_sdk import FTL\n\n# Create FTL application instance\nftl = FTL()\n\n@ftl.tool\ndef echo(message: str) -> str:\n \"\"\"Echo back the input.\"\"\"\n return f\"Echo: {message}\"\n\n# Create the Spin handler\nHandler = ftl.create_handler()\n```\n\n### 3. Build and Deploy\n\n```bash\n# Create virtual environment\npython3 -m venv venv\nsource venv/bin/activate\n\n# Install dependencies\npip install componentize-py spin-sdk ftl-sdk\n\n# Build\nftl build\n\n# Deploy to Fermyon Cloud\nftl deploy\n```\n\n## API Reference\n\n### Decorator-based API\n\n#### `FTL` Class\n\n```python\nftl = FTL()\n```\n\nCreates an FTL application instance for registering tools.\n\n#### `@ftl.tool` Decorator\n\n```python\n@ftl.tool\ndef my_tool(param: str) -> str:\n \"\"\"Tool description.\"\"\"\n return result\n\n# With custom name and annotations\n@ftl.tool(name=\"custom_name\", annotations={\"priority\": \"high\"})\ndef another_tool(data: dict) -> dict:\n return {\"processed\": data}\n```\n\nThe decorator:\n- Automatically generates JSON Schema from type hints\n- Extracts docstring as tool description\n- Handles both sync and async functions\n- Validates output against return type annotation\n\n#### `ftl.create_handler()`\n\nCreates a Spin HTTP handler that:\n- Returns tool metadata on GET / requests\n- Routes to specific tools on POST /{tool_name} requests\n- Handles async/await for async tool functions\n- Automatically converts return values to MCP format\n\n### `ToolResponse` Helper Methods\n\n```python\n# Simple text response\nToolResponse.text(\"Hello, world!\")\n\n# Error response\nToolResponse.error(\"Something went wrong\")\n\n# Response with structured content\nToolResponse.with_structured(\"Operation complete\", {\"result\": 42})\n```\n\n### `ToolContent` Helper Methods\n\n```python\n# Text content\nToolContent.text(\"Some text\", {\"priority\": 0.8})\n\n# Image content\nToolContent.image(base64_data, \"image/png\")\n\n# Audio content\nToolContent.audio(base64_data, \"audio/wav\")\n\n# Resource reference\nToolContent.resource({\"uri\": \"file:///example.txt\"})\n```\n\n### Type Guards\n\n```python\n# Check content types\nif is_text_content(content):\n print(content[\"text\"])\n```\n\n## Examples\n\n### Basic Tools\n\n```python\nfrom ftl_sdk import FTL\n\nftl = FTL()\n\n@ftl.tool\ndef echo(message: str) -> str:\n \"\"\"Echo the input.\"\"\"\n return f\"Echo: {message}\"\n\n@ftl.tool\ndef reverse_text(text: str) -> str:\n \"\"\"Reverse the input text.\"\"\"\n return text[::-1]\n\n@ftl.tool\ndef word_count(text: str) -> dict:\n \"\"\"Count words in text.\"\"\"\n count = len(text.split())\n return {\"text\": text, \"word_count\": count}\n\nHandler = ftl.create_handler()\n```\n\n### Async Tools\n\n```python\nimport asyncio\nfrom ftl_sdk import FTL\n\nftl = FTL()\n\n@ftl.tool\nasync def fetch_data(url: str) -> dict:\n \"\"\"Fetch data from URL asynchronously.\"\"\"\n # Simulate async HTTP request\n await asyncio.sleep(0.1)\n return {\n \"url\": url,\n \"status\": \"success\",\n \"data\": {\"example\": \"data\"}\n }\n\n@ftl.tool\nasync def process_items(items: list[str]) -> dict:\n \"\"\"Process items with async operations.\"\"\"\n results = []\n for item in items:\n # Simulate async processing\n await asyncio.sleep(0.01)\n results.append(item.upper())\n \n return {\n \"original\": items,\n \"processed\": results,\n \"count\": len(results)\n }\n\n# Mix sync and async tools\n@ftl.tool\ndef sync_add(a: int, b: int) -> int:\n \"\"\"Add two numbers synchronously.\"\"\"\n return a + b\n\nHandler = ftl.create_handler()\n```\n\n### Error Handling\n\n```python\nfrom ftl_sdk import FTL\n\nftl = FTL()\n\n@ftl.tool\ndef divide(a: float, b: float) -> float:\n \"\"\"Divide two numbers.\"\"\"\n if b == 0:\n raise ValueError(\"Cannot divide by zero\")\n return a / b\n\n# Async error handling\n@ftl.tool\nasync def validate_data(data: dict) -> dict:\n \"\"\"Validate data asynchronously.\"\"\"\n if \"required_field\" not in data:\n raise KeyError(\"Missing required field: required_field\")\n \n # Simulate async validation\n await asyncio.sleep(0.05)\n \n if not isinstance(data[\"required_field\"], str):\n raise TypeError(\"required_field must be a string\")\n \n return {\"status\": \"valid\", \"data\": data}\n\nHandler = ftl.create_handler()\n```\n\nThe FTL framework automatically catches exceptions and returns them as error responses.\n\n## Development\n\n### Setting Up Development Environment\n\n1. **Clone the repository**:\n ```bash\n git clone https://github.com/fastertools/ftl-cli.git\n cd ftl-cli/sdk/python\n ```\n\n2. **Create virtual environment**:\n ```bash\n python -m venv venv\n source venv/bin/activate # On Windows: venv\\Scripts\\activate\n ```\n\n3. **Install development dependencies**:\n ```bash\n make install-dev\n # or manually:\n pip install -e \".[dev]\"\n pip install componentize-py\n ```\n\n### Running Tests\n\n```bash\n# Run all tests\nmake test\n\n# Run tests with coverage\nmake test-cov\n\n# Run specific test\npytest tests/test_ftl_sdk.py::test_tool_response_text\n```\n\n### Code Quality\n\n```bash\n# Format code\nmake format\n\n# Run linting\nmake lint\n\n# Type checking\nmake type-check\n\n# Run all quality checks\nmake quality\n```\n\n### Available Make Commands\n\n```bash\nmake help # Show all available commands\nmake install # Install SDK\nmake install-dev # Install with dev dependencies\nmake format # Format code with black\nmake lint # Run linting with ruff\nmake type-check # Run type checking with mypy\nmake test # Run tests\nmake test-cov # Run tests with coverage\nmake clean # Clean build artifacts\nmake build # Build distribution packages\nmake publish # Publish to PyPI\n```\n\n## Building to WebAssembly\n\nTools must be compiled to WebAssembly to run on Spin:\n\n1. **Install dependencies**:\n ```bash\n pip install componentize-py spin-sdk ftl-sdk\n ```\n\n2. **Build with componentize-py**:\n ```bash\n componentize-py -w spin-http componentize app -o app.wasm\n ```\n\n3. **Or use FTL CLIs build command**:\n ```bash\n ftl build\n ```\n\n## Best Practices\n\n### Type Hints\n\nAlways use type hints for better code clarity and IDE support:\n\n```python\nfrom typing import Dict, Any\nfrom ftl_sdk import ToolResponse\n\ndef my_handler(input_data: Dict[str, Any]) -> Dict[str, Any]:\n message: str = input_data.get(\"message\", \"\")\n return ToolResponse.text(f\"Received: {message}\")\n```\n\n### Error Handling\n\nHandle errors gracefully and return informative error messages:\n\n```python\ndef safe_handler(input_data: Dict[str, Any]) -> Dict[str, Any]:\n try:\n # Validate required fields\n if \"required_field\" not in input_data:\n return ToolResponse.error(\"Missing required field: required_field\")\n \n # Process input\n result = process_data(input_data[\"required_field\"])\n return ToolResponse.text(f\"Success: {result}\")\n \n except ValueError as e:\n return ToolResponse.error(f\"Invalid value: {e}\")\n except Exception as e:\n return ToolResponse.error(f\"Unexpected error: {e}\")\n```\n\n### Testing Your Tools\n\nWrite comprehensive tests for your tools:\n\n```python\nimport pytest\nfrom your_module import your_handler\n\ndef test_handler_success():\n result = your_handler({\"message\": \"test\"})\n assert result[\"content\"][0][\"text\"] == \"Expected output\"\n\ndef test_handler_missing_field():\n result = your_handler({})\n assert result.get(\"isError\") is True\n assert \"Missing required field\" in result[\"content\"][0][\"text\"]\n```\n\n## Important Notes\n\n1. **Python Version**: Requires Python 3.10 or later. Python 3.11+ recommended.\n\n2. **Zero Dependencies**: This SDK has no external dependencies beyond `spin-sdk`, keeping the WASM bundle size minimal.\n\n3. **Input Validation**: The FTL gateway handles input validation against your JSON Schema. Your handler can assume inputs are valid.\n\n4. **Virtual Environments**: Always use a virtual environment to ensure consistent builds.\n\n5. **WASM Size**: Python WASM components are larger than TypeScript/Rust equivalents (~37MB), but this is acceptable for cloud deployment.\n\n6. **Type Safety**: Use type hints and mypy for better code quality and fewer runtime errors.\n\n7. **Code Quality**: The SDK includes development tools (black, ruff, mypy, pytest) to maintain high code quality standards.\n\n## Deployment\n\nDeploy to FTL:\n\n```bash\n# Deploy with auto-generated name\nftl deploy\n```\n\n## Development\n\nContributions are welcome! Please feel free to submit a Pull Request.\n\n### Setting Up Development Environment\n\n```bash\n# Clone the repository\ngit clone https://github.com/fastertools/ftl-cli.git\ncd ftl-cli/sdk/python\n\n# Create virtual environment\npython -m venv venv\nsource venv/bin/activate # On Windows: venv\\Scripts\\activate\n\n# Install development dependencies\npip install -e \".[dev]\"\npip install tox\n```\n\n### Running Tests\n\n```bash\n# Run tests for all Python versions\ntox\n\n# Run tests for specific Python version\ntox -e py311\n\n# Run linting and type checking\ntox -e lint,type\n\n# Run tests with coverage\ntox -e py311 -- --cov-report=html\n```\n\n### Code Quality\n\n```bash\n# Format code\nblack src tests\n\n# Run linter\nruff check src tests\n\n# Type checking\nmypy src\n```\n\n## Changelog\n\nSee [CHANGELOG.md](CHANGELOG.md) for a list of changes in each release.\n\n## License\n\nApache-2.0 - see [LICENSE](../../LICENSE) for details.\n",
"bugtrack_url": null,
"license": "Apache-2.0",
"summary": "Python SDK for building MCP tools that compile to WebAssembly",
"version": "0.1.0",
"project_urls": {
"Documentation": "https://github.com/fastertools/ftl-cli/tree/main/sdk/python",
"Homepage": "https://github.com/fastertools/ftl-cli",
"Issues": "https://github.com/fastertools/ftl-cli/issues",
"Repository": "https://github.com/fastertools/ftl-cli"
},
"split_keywords": [
"ftl",
" mcp",
" wasm",
" webassembly",
" spin",
" fermyon",
" model-context-protocol"
],
"urls": [
{
"comment_text": null,
"digests": {
"blake2b_256": "73ffdd6383c48ab71f043b3084c03eab6d8d8086183f59ea32e0d440b50782d8",
"md5": "30623a6745e1218ad3608c2023075e51",
"sha256": "849d020e988542b17dc4007a83b3a491cc497d8b7b33ea0b0feeb97aff959670"
},
"downloads": -1,
"filename": "ftl_sdk-0.1.0-py3-none-any.whl",
"has_sig": false,
"md5_digest": "30623a6745e1218ad3608c2023075e51",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.10",
"size": 14242,
"upload_time": "2025-08-04T03:54:27",
"upload_time_iso_8601": "2025-08-04T03:54:27.994681Z",
"url": "https://files.pythonhosted.org/packages/73/ff/dd6383c48ab71f043b3084c03eab6d8d8086183f59ea32e0d440b50782d8/ftl_sdk-0.1.0-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": null,
"digests": {
"blake2b_256": "a938de3cf931134c01d5dc4ff34890503faf2ad3d9c393a3fb82453dceca2f99",
"md5": "870d4d59e1951157d0202f472959f135",
"sha256": "beec6090e1cf5940be808ef8552458a679a17beb692f783977adbbec59c799a4"
},
"downloads": -1,
"filename": "ftl_sdk-0.1.0.tar.gz",
"has_sig": false,
"md5_digest": "870d4d59e1951157d0202f472959f135",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.10",
"size": 21980,
"upload_time": "2025-08-04T03:54:29",
"upload_time_iso_8601": "2025-08-04T03:54:29.293918Z",
"url": "https://files.pythonhosted.org/packages/a9/38/de3cf931134c01d5dc4ff34890503faf2ad3d9c393a3fb82453dceca2f99/ftl_sdk-0.1.0.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2025-08-04 03:54:29",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "fastertools",
"github_project": "ftl-cli",
"github_not_found": true,
"lcname": "ftl-sdk"
}