crewai-clawfetch 0.1.0__py3-none-any.whl

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,24 @@
1
+ """CrewAI integration for ClawFetch — pay-per-call web intelligence for AI agents."""
2
+
3
+ from crewai_clawfetch.tools import (
4
+ ClawFetchDomainsCheckTool,
5
+ ClawFetchDomainsSuggestTool,
6
+ ClawFetchExtractTool,
7
+ ClawFetchFetchTool,
8
+ ClawFetchParseTool,
9
+ ClawFetchRenderTool,
10
+ ClawFetchResearchTool,
11
+ clawfetch_tools,
12
+ )
13
+
14
+ __all__ = [
15
+ "ClawFetchFetchTool",
16
+ "ClawFetchRenderTool",
17
+ "ClawFetchExtractTool",
18
+ "ClawFetchResearchTool",
19
+ "ClawFetchParseTool",
20
+ "ClawFetchDomainsCheckTool",
21
+ "ClawFetchDomainsSuggestTool",
22
+ "clawfetch_tools",
23
+ ]
24
+ __version__ = "0.1.0"
File without changes
@@ -0,0 +1,230 @@
1
+ """CrewAI tools for the ClawFetch web intelligence API (x402, USDC on Base)."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import json
6
+ import os
7
+ from typing import Any, List, Optional, Type
8
+
9
+ from clawfetch import ClawFetch
10
+ from crewai.tools import BaseTool
11
+ from pydantic import BaseModel, ConfigDict, Field
12
+
13
+ DEFAULT_MAX_CHARS = 10_000
14
+
15
+ EXTRACT_TYPES = (
16
+ "twitter, coingecko, github, news, npm, pypi, crates, hackernews, youtube, "
17
+ "wikipedia, producthunt, imdb, amazon, crunchbase, sec-edgar, zillow, redfin"
18
+ )
19
+
20
+
21
+ def _make_client(private_key: Optional[str]) -> ClawFetch:
22
+ key = private_key or os.environ.get("CLAWFETCH_PRIVATE_KEY")
23
+ if not key:
24
+ raise ValueError("Pass private_key=..., set CLAWFETCH_PRIVATE_KEY, or pass client=ClawFetch(...).")
25
+ return ClawFetch(private_key=key)
26
+
27
+
28
+ def _truncate(text: str, max_chars: int) -> str:
29
+ if len(text) <= max_chars:
30
+ return text
31
+ return text[:max_chars] + f"\n...[truncated {len(text) - max_chars} chars]"
32
+
33
+
34
+ def _page(result: dict) -> str:
35
+ return f"# {result.get('title') or ''}\nURL: {result.get('url') or ''}\n\n{result.get('text') or ''}"
36
+
37
+
38
+ # ─── Input schemas ────────────────────────────────────────────────
39
+
40
+
41
+ class FetchInput(BaseModel):
42
+ url: str = Field(description="Absolute http(s) URL of the page to fetch.")
43
+
44
+
45
+ class RenderInput(BaseModel):
46
+ url: str = Field(description="Absolute http(s) URL of the JavaScript-heavy page.")
47
+ wait_for: Optional[str] = Field(
48
+ default=None, description="Optional CSS selector to wait for before reading the page."
49
+ )
50
+
51
+
52
+ class ExtractInput(BaseModel):
53
+ url: str = Field(description="URL on a supported site (e.g. a GitHub repo or CoinGecko coin page).")
54
+ type: Optional[str] = Field(
55
+ default=None,
56
+ description=f"Optional extractor name; auto-detected from the URL when omitted. One of: {EXTRACT_TYPES}.",
57
+ )
58
+
59
+
60
+ class ResearchInput(BaseModel):
61
+ query: str = Field(description="Research question or topic.")
62
+ max_results: Optional[int] = Field(
63
+ default=None, ge=1, le=10, description="Number of sources to read (1-10, default 5)."
64
+ )
65
+
66
+
67
+ class ParseInput(BaseModel):
68
+ url: str = Field(description="Absolute http(s) URL of an office document or PDF.")
69
+ format: Optional[str] = Field(
70
+ default=None,
71
+ description="Optional format override (doc, docx, odt, pdf, ppt, pptx, rtf, epub, xlsx, ods, odp, csv).",
72
+ )
73
+
74
+
75
+ class DomainsCheckInput(BaseModel):
76
+ domains: List[str] = Field(description="Fully qualified domain names to check, e.g. ['acme.ai', 'acme.com'] (max 20).")
77
+
78
+
79
+ class DomainsSuggestInput(BaseModel):
80
+ keywords: List[str] = Field(description="Keywords to combine into domain names, e.g. ['agent', 'grid'].")
81
+ tlds: Optional[List[str]] = Field(
82
+ default=None, description="TLDs to try without dots, e.g. ['com', 'ai']. Default: com, ai, io."
83
+ )
84
+ max_check: Optional[int] = Field(
85
+ default=None, ge=1, le=50, description="How many generated names to check for availability (1-50, default 30)."
86
+ )
87
+
88
+
89
+ # ─── Tools ────────────────────────────────────────────────────────
90
+
91
+
92
+ class _ClawFetchTool(BaseTool):
93
+ """Shared client wiring and output formatting.
94
+
95
+ Construct with ``private_key=`` (or the ``CLAWFETCH_PRIVATE_KEY`` env var)
96
+ or an existing ``client=ClawFetch(...)``.
97
+ """
98
+
99
+ model_config = ConfigDict(arbitrary_types_allowed=True)
100
+
101
+ private_key: Optional[str] = Field(default=None, exclude=True, repr=False)
102
+ client: Optional[ClawFetch] = Field(default=None, exclude=True)
103
+ max_chars: int = DEFAULT_MAX_CHARS
104
+
105
+ def model_post_init(self, __context: Any) -> None:
106
+ super().model_post_init(__context)
107
+ if self.client is None:
108
+ self.client = _make_client(self.private_key)
109
+
110
+ def _json(self, result: Any) -> str:
111
+ return _truncate(json.dumps(result, ensure_ascii=False), self.max_chars)
112
+
113
+
114
+ class ClawFetchFetchTool(_ClawFetchTool):
115
+ name: str = "clawfetch_fetch"
116
+ description: str = (
117
+ "Fetch a web page and return its readable text with title and URL. "
118
+ "Fast, no JavaScript execution; use clawfetch_render for JS-heavy pages. "
119
+ "Costs $0.001 USDC per call."
120
+ )
121
+ args_schema: Type[BaseModel] = FetchInput
122
+
123
+ def _run(self, url: str) -> str:
124
+ return _truncate(_page(self.client.fetch(url, max_chars=self.max_chars)), self.max_chars)
125
+
126
+
127
+ class ClawFetchRenderTool(_ClawFetchTool):
128
+ name: str = "clawfetch_render"
129
+ description: str = (
130
+ "Render a JavaScript-heavy page in a headless browser and return its visible text. "
131
+ "Use when clawfetch_fetch returns empty or incomplete content. Costs $0.005 USDC per call."
132
+ )
133
+ args_schema: Type[BaseModel] = RenderInput
134
+
135
+ def _run(self, url: str, wait_for: Optional[str] = None) -> str:
136
+ result = self.client.render(url, max_chars=self.max_chars, wait_for=wait_for)
137
+ return _truncate(_page(result), self.max_chars)
138
+
139
+
140
+ class ClawFetchExtractTool(_ClawFetchTool):
141
+ name: str = "clawfetch_extract"
142
+ description: str = (
143
+ "Extract typed structured data (JSON) from a page on a supported site: "
144
+ f"{EXTRACT_TYPES}. Costs $0.008 USDC per call."
145
+ )
146
+ args_schema: Type[BaseModel] = ExtractInput
147
+
148
+ def _run(self, url: str, type: Optional[str] = None) -> str:
149
+ return self._json(self.client.extract(url, type=type))
150
+
151
+
152
+ class ClawFetchResearchTool(_ClawFetchTool):
153
+ name: str = "clawfetch_research"
154
+ description: str = (
155
+ "Search the web for a query and return the text of the top sources with their URLs "
156
+ "(for citations). Costs $0.02 USDC per call."
157
+ )
158
+ args_schema: Type[BaseModel] = ResearchInput
159
+
160
+ def _run(self, query: str, max_results: Optional[int] = None) -> str:
161
+ return self._json(self.client.research(query, max_results=max_results))
162
+
163
+
164
+ class ClawFetchParseTool(_ClawFetchTool):
165
+ name: str = "clawfetch_parse"
166
+ description: str = (
167
+ "Convert an office document or PDF at a URL (docx, pptx, xlsx, pdf, odt, rtf, epub, csv, ...) "
168
+ "to Markdown. No OCR: scanned PDFs fail. Costs $0.005 USDC per call."
169
+ )
170
+ args_schema: Type[BaseModel] = ParseInput
171
+
172
+ def _run(self, url: str, format: Optional[str] = None) -> str:
173
+ return _truncate(self.client.parse(url, format=format).get("markdown") or "", self.max_chars)
174
+
175
+
176
+ class ClawFetchDomainsCheckTool(_ClawFetchTool):
177
+ name: str = "clawfetch_domains_check"
178
+ description: str = (
179
+ "Check whether specific domain names are available to register. Returns JSON with a "
180
+ "result per domain (available true/false, or null with an error). Costs $0.003 USDC per call."
181
+ )
182
+ args_schema: Type[BaseModel] = DomainsCheckInput
183
+
184
+ def _run(self, domains: List[str]) -> str:
185
+ return self._json(self.client.domains_check(domains))
186
+
187
+
188
+ class ClawFetchDomainsSuggestTool(_ClawFetchTool):
189
+ name: str = "clawfetch_domains_suggest"
190
+ description: str = (
191
+ "Generate domain names from keywords and check which are available. Returns JSON with the "
192
+ "list of available domains and every checked result. Costs $0.003 USDC per call."
193
+ )
194
+ args_schema: Type[BaseModel] = DomainsSuggestInput
195
+
196
+ def _run(
197
+ self,
198
+ keywords: List[str],
199
+ tlds: Optional[List[str]] = None,
200
+ max_check: Optional[int] = None,
201
+ ) -> str:
202
+ return self._json(self.client.domains_suggest(keywords, tlds=tlds, max_check=max_check))
203
+
204
+
205
+ TOOL_CLASSES = (
206
+ ClawFetchFetchTool,
207
+ ClawFetchRenderTool,
208
+ ClawFetchExtractTool,
209
+ ClawFetchResearchTool,
210
+ ClawFetchParseTool,
211
+ ClawFetchDomainsCheckTool,
212
+ ClawFetchDomainsSuggestTool,
213
+ )
214
+
215
+
216
+ def clawfetch_tools(
217
+ private_key: Optional[str] = None,
218
+ client: Optional[ClawFetch] = None,
219
+ max_chars: int = DEFAULT_MAX_CHARS,
220
+ ) -> List[BaseTool]:
221
+ """Return every ClawFetch tool, sharing one client.
222
+
223
+ Args:
224
+ private_key: Base wallet private key holding USDC. Defaults to the
225
+ ``CLAWFETCH_PRIVATE_KEY`` environment variable.
226
+ client: An existing ``clawfetch.ClawFetch`` client (overrides private_key).
227
+ max_chars: Maximum characters returned by each tool call.
228
+ """
229
+ client = client or _make_client(private_key)
230
+ return [cls(client=client, max_chars=max_chars) for cls in TOOL_CLASSES]
@@ -0,0 +1,87 @@
1
+ Metadata-Version: 2.5
2
+ Name: crewai-clawfetch
3
+ Version: 0.1.0
4
+ Summary: CrewAI tools for ClawFetch — pay-per-call web intelligence for AI agents (x402, USDC on Base)
5
+ Project-URL: Homepage, https://api.clawfetch.ai
6
+ License-Expression: MIT
7
+ License-File: LICENSE
8
+ Keywords: ai-agents,clawfetch,crewai,tools,web-scraping,x402
9
+ Classifier: Development Status :: 3 - Alpha
10
+ Classifier: Intended Audience :: Developers
11
+ Classifier: Programming Language :: Python :: 3
12
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
13
+ Requires-Python: >=3.10
14
+ Requires-Dist: clawfetch<0.3,>=0.2.2
15
+ Requires-Dist: crewai<2,>=1.0
16
+ Provides-Extra: test
17
+ Requires-Dist: pytest>=8; extra == 'test'
18
+ Description-Content-Type: text/markdown
19
+
20
+ # crewai-clawfetch
21
+
22
+ CrewAI tools for [ClawFetch](https://api.clawfetch.ai), a pay-per-call web intelligence API for AI agents. Each call is paid with an x402 micropayment in USDC on Base.
23
+
24
+ ## Install
25
+
26
+ ```bash
27
+ pip install crewai-clawfetch
28
+ ```
29
+
30
+ ## Wallet
31
+
32
+ You need a Base wallet private key that holds USDC. There is no API key and no signup. Payments are gasless EIP-3009 transfers signed locally by the [`clawfetch`](https://pypi.org/project/clawfetch/) SDK, so the wallet does not need ETH. Use a dedicated wallet with a small balance.
33
+
34
+ ```bash
35
+ export CLAWFETCH_PRIVATE_KEY=0x...
36
+ ```
37
+
38
+ ## Quickstart
39
+
40
+ ```python
41
+ from crewai import Agent
42
+ from crewai_clawfetch import clawfetch_tools
43
+
44
+ tools = clawfetch_tools() # reads CLAWFETCH_PRIVATE_KEY
45
+ # or: clawfetch_tools(private_key="0x...", max_chars=8000)
46
+
47
+ researcher = Agent(
48
+ role="Researcher",
49
+ goal="Answer questions with cited web sources",
50
+ backstory="You read primary sources before answering.",
51
+ tools=tools,
52
+ )
53
+ ```
54
+
55
+ You can also create a single tool, or pass an existing client:
56
+
57
+ ```python
58
+ from clawfetch import ClawFetch
59
+ from crewai_clawfetch import ClawFetchFetchTool
60
+
61
+ fetch = ClawFetchFetchTool(client=ClawFetch(private_key="0x..."))
62
+ print(fetch.run(url="https://example.com"))
63
+ ```
64
+
65
+ ## Tools
66
+
67
+ | Tool | Name | Price per call | What it does |
68
+ |---|---|---|---|
69
+ | `ClawFetchFetchTool` | `clawfetch_fetch` | $0.001 | URL to readable text (no JavaScript) |
70
+ | `ClawFetchRenderTool` | `clawfetch_render` | $0.005 | JavaScript-rendered page to text |
71
+ | `ClawFetchExtractTool` | `clawfetch_extract` | $0.008 | Typed JSON from 17 supported sites |
72
+ | `ClawFetchResearchTool` | `clawfetch_research` | $0.02 | Web search plus source texts for citations |
73
+ | `ClawFetchParseTool` | `clawfetch_parse` | $0.005 | Office document or PDF at a URL to Markdown (no OCR) |
74
+ | `ClawFetchDomainsCheckTool` | `clawfetch_domains_check` | $0.003 | Domain availability |
75
+ | `ClawFetchDomainsSuggestTool` | `clawfetch_domains_suggest` | $0.003 | Generate and check domain names from keywords |
76
+
77
+ Supported `clawfetch_extract` sites: twitter, coingecko, github, news, npm, pypi, crates, hackernews, youtube, wikipedia, producthunt, imdb, amazon, crunchbase, sec-edgar, zillow, redfin.
78
+
79
+ ## Output and errors
80
+
81
+ Tools return a string. Fetch and render return the page title, URL and text. Parse returns Markdown. The other tools return the API response as JSON. Output is cut at `max_chars` (default 10,000).
82
+
83
+ SDK errors (`PaymentError`, `RateLimitError`, `ApiError`, `NetworkError`, all subclasses of `clawfetch.ClawFetchError`) are raised unchanged.
84
+
85
+ ## License
86
+
87
+ MIT
@@ -0,0 +1,7 @@
1
+ crewai_clawfetch/__init__.py,sha256=aUpgF9Qdaz6cJ38VMNui-muClymjGYLop3happ1czbs,603
2
+ crewai_clawfetch/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
3
+ crewai_clawfetch/tools.py,sha256=4LgG6isnvXXgBC60_vofAYWG75xGO9ih3gf9nIfcKeM,8525
4
+ crewai_clawfetch-0.1.0.dist-info/METADATA,sha256=n6TOqkviV5lDtKBlopjsDTCVWf68MK0Cy7t8icpzCJw,3321
5
+ crewai_clawfetch-0.1.0.dist-info/WHEEL,sha256=W3fkpkm7-wf9vBI5Z-7s0eWkeM-spu78I8Neb98DeEg,87
6
+ crewai_clawfetch-0.1.0.dist-info/licenses/LICENSE,sha256=WH3iSfCwXYUH5l2vPCR2YVXjMEFplMYc4cDAEF_Vnwg,1066
7
+ crewai_clawfetch-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.32.4
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 ClawFetch
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.