commit f74991da27628794534cddb2cb8a45789ff7b0a3 Author: zlei9 Date: Sun Mar 29 09:46:10 2026 +0800 Initial commit with translated description diff --git a/README.md b/README.md new file mode 100644 index 0000000..6522506 --- /dev/null +++ b/README.md @@ -0,0 +1,70 @@ +# QVeris Skill for Claude Code + +[中文文档](README.zh-CN.md) | English + +A skill that enables Claude Code to dynamically search and execute tools via the QVeris API. + +## Features + +- **Tool Discovery**: Search for APIs by describing what you need +- **Tool Execution**: Execute any discovered tool with parameters +- **Wide Coverage**: Access weather, stocks, search, currency, and thousands more APIs + +## Installation + +### Prerequisites + +This skill requires `uv`, a fast Python package manager. Install it first: + +**macOS and Linux:** +```bash +curl -LsSf https://astral.sh/uv/install.sh | sh +``` + +**Windows:** +```powershell +powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex" +``` + +For other installation methods, see the [official uv installation guide](https://docs.astral.sh/uv/getting-started/installation/). + +### Install the Skill + +1. Copy this folder to your Claude Code skills directory: + ```bash + cp -r qveris ~/.claude/skills/ + ``` + +2. Set your API key: + ```bash + export QVERIS_API_KEY="your-api-key-here" + ``` + + Get your API key at https://qveris.ai + +## Usage + +Once installed, Claude Code will automatically use this skill when you ask questions about: +- Weather data +- Stock prices and market analysis +- Web searches +- Currency exchange rates +- And more... + +### Manual Commands + +```bash +# Search for tools +uv run scripts/qveris_tool.py search "stock price data" + +# Execute a tool +uv run scripts/qveris_tool.py execute --search-id --params '{"symbol": "AAPL"}' +``` + +## Author + +[@hqmank](https://x.com/hqmank) + +## License + +MIT diff --git a/README.zh-CN.md b/README.zh-CN.md new file mode 100644 index 0000000..bd759aa --- /dev/null +++ b/README.zh-CN.md @@ -0,0 +1,70 @@ +# QVeris Skill for Claude Code + +中文文档 | [English](README.md) + +这是一个让 Claude Code 能够通过 QVeris API 动态搜索和执行工具的技能。 + +## 功能特性 + +- **工具发现**:通过描述您的需求来搜索 API +- **工具执行**:使用参数执行任何已发现的工具 +- **广泛覆盖**:访问天气、股票、搜索、货币以及数千种其他 API + +## 安装 + +### 前置要求 + +此技能需要 `uv`,一个快速的 Python 包管理工具。请先安装它: + +**macOS 和 Linux:** +```bash +curl -LsSf https://astral.sh/uv/install.sh | sh +``` + +**Windows:** +```powershell +powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex" +``` + +更多安装方式,请查看 [uv 官方安装指南](https://docs.astral.sh/uv/getting-started/installation/)。 + +### 安装技能 + +1. 将此文件夹复制到您的 Claude Code 技能目录: + ```bash + cp -r qveris ~/.claude/skills/ + ``` + +2. 设置您的 API 密钥: + ```bash + export QVERIS_API_KEY="your-api-key-here" + ``` + + 在 https://qveris.ai 获取您的 API 密钥 + +## 使用方法 + +安装完成后,当您询问以下问题时,Claude Code 将自动使用此技能: +- 天气数据 +- 股票价格和市场分析 +- 网络搜索 +- 货币汇率 +- 更多... + +### 手动命令 + +```bash +# 搜索工具 +uv run scripts/qveris_tool.py search "stock price data" + +# 执行工具 +uv run scripts/qveris_tool.py execute --search-id --params '{"symbol": "AAPL"}' +``` + +## 作者 + +[@hqmank](https://x.com/hqmank) + +## 许可证 + +MIT diff --git a/SKILL.md b/SKILL.md new file mode 100644 index 0000000..8a5e1a0 --- /dev/null +++ b/SKILL.md @@ -0,0 +1,87 @@ +--- +name: qveris +description: "通过QVeris API搜索和执行动态工具。在需要查找和调用外部API/工具时使用。" +triggers: + - pattern: "股票|stock|股价|股市" + description: "检测股票相关查询" + - pattern: "交易|trading|买卖|成交" + description: "检测交易相关查询" + - pattern: "分析|analysis|数据|指标|技术分析|基本面" + description: "检测分析相关查询" + - pattern: "市值|涨跌|收盘|开盘|市盈率|pe|pb" + description: "检测股票指标查询" +auto_invoke: true +examples: + - "帮我查一下特斯拉的股价" + - "分析一下苹果公司的财报数据" + - "查询今日A股涨停板" + - "获取比特币实时价格" +--- + +# QVeris Tool Search & Execution + +QVeris provides dynamic tool discovery and execution - search for tools by capability, then execute them with parameters. + +## Setup + +Requires environment variable: +- `QVERIS_API_KEY` - Get from https://qveris.ai + +## Quick Start + +### Search for tools +```bash +uv run scripts/qveris_tool.py search "weather forecast API" +``` + +### Execute a tool +```bash +uv run scripts/qveris_tool.py execute openweathermap_current_weather --search-id --params '{"city": "London", "units": "metric"}' +``` + +## Script Usage + +``` +scripts/qveris_tool.py [options] + +Commands: + search Search for tools matching a capability description + execute Execute a specific tool with parameters + +Options: + --limit N Max results for search (default: 5) + --search-id ID Search ID from previous search (required for execute) + --params JSON Tool parameters as JSON string + --max-size N Max response size in bytes (default: 20480) + --json Output raw JSON instead of formatted display +``` + +## Workflow + +1. **Search**: Describe the capability needed (not specific parameters) + - Good: "weather forecast API" + - Bad: "get weather for London" + +2. **Select**: Review tools by `success_rate` and `avg_execution_time` + +3. **Execute**: Call tool with `tool_id`, `search_id`, and `parameters` + +## Example Session + +```bash +# Find weather tools +uv run scripts/qveris_tool.py search "current weather data" + +# Execute with returned tool_id and search_id +uv run scripts/qveris_tool.py execute openweathermap_current_weather \ + --search-id abc123 \ + --params '{"city": "Tokyo", "units": "metric"}' +``` + +## Use Cases + +- **Weather Data**: Get current weather, forecasts for any location +- **Stock Market**: Query stock prices, historical data, earnings calendars +- **Search**: Web search, news retrieval +- **Data APIs**: Currency exchange, geolocation, translations +- **And more**: QVeris aggregates thousands of API tools diff --git a/_meta.json b/_meta.json new file mode 100644 index 0000000..7893a19 --- /dev/null +++ b/_meta.json @@ -0,0 +1,6 @@ +{ + "ownerId": "kn7cpk0s04wpxzfp5m4eefgzj98024p8", + "slug": "qveris", + "version": "0.1.0", + "publishedAt": 1769858151112 +} \ No newline at end of file diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..fbf593d --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,8 @@ +[project] +name = "qveris-skill" +version = "0.1.0" +description = "QVeris API tool search and execution skill for Claude Code" +requires-python = ">=3.10" +dependencies = [ + "httpx>=0.25.0", +] diff --git a/scripts/qveris_tool.py b/scripts/qveris_tool.py new file mode 100644 index 0000000..2f8b098 --- /dev/null +++ b/scripts/qveris_tool.py @@ -0,0 +1,218 @@ +#!/usr/bin/env python3 +# /// script +# requires-python = ">=3.10" +# dependencies = [ +# "httpx", +# ] +# /// +""" +QVeris Tool Search & Execution CLI + +Search for tools by capability and execute them via QVeris API. + +Usage: + uv run qveris_tool.py search "weather forecast" + uv run qveris_tool.py execute --search-id --params '{"city": "London"}' +""" + +import argparse +import asyncio +import json +import os +import sys + +import httpx + +BASE_URL = "https://qveris.ai/api/v1" + + +def get_api_key() -> str: + """Get QVeris API key from environment.""" + key = os.environ.get("QVERIS_API_KEY") + if not key: + print("Error: QVERIS_API_KEY environment variable not set", file=sys.stderr) + print("Get your API key at https://qveris.ai", file=sys.stderr) + sys.exit(1) + return key + + +async def search_tools(query: str, limit: int = 5) -> dict: + """Search for tools matching a capability description.""" + api_key = get_api_key() + + async with httpx.AsyncClient(timeout=30) as client: + response = await client.post( + f"{BASE_URL}/search", + headers={ + "Authorization": f"Bearer {api_key}", + "Content-Type": "application/json", + }, + json={"query": query, "limit": limit}, + ) + response.raise_for_status() + return response.json() + + +async def execute_tool( + tool_id: str, + search_id: str, + parameters: dict, + max_response_size: int = 20480, +) -> dict: + """Execute a specific tool with parameters.""" + api_key = get_api_key() + + async with httpx.AsyncClient(timeout=60) as client: + response = await client.post( + f"{BASE_URL}/tools/execute", + params={"tool_id": tool_id}, + headers={ + "Authorization": f"Bearer {api_key}", + "Content-Type": "application/json", + }, + json={ + "search_id": search_id, + "parameters": parameters, + "max_response_size": max_response_size, + }, + ) + response.raise_for_status() + return response.json() + + +def display_search_results(result: dict) -> None: + """Display search results in a formatted way.""" + search_id = result.get("search_id", "N/A") + tools = result.get("results", []) + total = result.get("total", len(tools)) + + print(f"\nSearch ID: {search_id}") + print(f"Found {total} tools\n") + + if not tools: + print("No tools found.") + return + + for i, tool in enumerate(tools, 1): + tool_id = tool.get("tool_id", "N/A") + name = tool.get("name", "N/A") + desc = tool.get("description", "N/A") + + # Stats + stats = tool.get("stats", {}) + success_rate = stats.get("success_rate", "N/A") + avg_time = stats.get("avg_execution_time_ms", "N/A") + + if isinstance(success_rate, (int, float)): + success_rate = f"{success_rate:.0%}" + if isinstance(avg_time, (int, float)): + avg_time = f"{avg_time:.1f}ms" + + print(f"[{i}] {name}") + print(f" ID: {tool_id}") + print(f" {desc[:100]}{'...' if len(desc) > 100 else ''}") + print(f" Success: {success_rate} | Avg Time: {avg_time}") + + # Show params + params = tool.get("params", []) + if params: + required = [p["name"] for p in params if p.get("required")] + optional = [p["name"] for p in params if not p.get("required")] + if required: + print(f" Required: {', '.join(required)}") + if optional: + print(f" Optional: {', '.join(optional[:5])}{'...' if len(optional) > 5 else ''}") + + # Show example + examples = tool.get("examples", {}) + sample = examples.get("sample_parameters") + if sample: + print(f" Example: {json.dumps(sample)}") + + print() + + +def display_execution_result(result: dict) -> None: + """Display tool execution result.""" + success = result.get("success", False) + exec_time = result.get("elapsed_time_ms", "N/A") + cost = result.get("cost", 0) + + status = "Success" if success else "Failed" + print(f"\n{status}") + print(f"Time: {exec_time}ms | Cost: {cost}") + + if not success: + error = result.get("error_message", "Unknown error") + print(f"Error: {error}") + + data = result.get("result", {}) + if data: + print("\nResult:") + print(json.dumps(data, indent=2, ensure_ascii=False)) + + +async def main(): + parser = argparse.ArgumentParser( + description="QVeris Tool Search & Execution CLI", + formatter_class=argparse.RawDescriptionHelpFormatter, + epilog=""" +Examples: + %(prog)s search "weather forecast API" + %(prog)s execute openweathermap_current_weather --search-id abc123 --params '{"city": "London"}' + """, + ) + + subparsers = parser.add_subparsers(dest="command", required=True) + + # Search command + search_parser = subparsers.add_parser("search", help="Search for tools") + search_parser.add_argument("query", help="Capability description to search for") + search_parser.add_argument("--limit", type=int, default=5, help="Max results (default: 5)") + search_parser.add_argument("--json", action="store_true", help="Output raw JSON") + + # Execute command + exec_parser = subparsers.add_parser("execute", help="Execute a tool") + exec_parser.add_argument("tool_id", help="Tool ID to execute") + exec_parser.add_argument("--search-id", required=True, help="Search ID from previous search") + exec_parser.add_argument("--params", default="{}", help="Tool parameters as JSON string") + exec_parser.add_argument("--max-size", type=int, default=20480, help="Max response size") + exec_parser.add_argument("--json", action="store_true", help="Output raw JSON") + + args = parser.parse_args() + + try: + if args.command == "search": + result = await search_tools(args.query, args.limit) + if args.json: + print(json.dumps(result, indent=2, ensure_ascii=False)) + else: + display_search_results(result) + + elif args.command == "execute": + params = json.loads(args.params) + result = await execute_tool( + args.tool_id, + args.search_id, + params, + args.max_size, + ) + if args.json: + print(json.dumps(result, indent=2, ensure_ascii=False)) + else: + display_execution_result(result) + + except httpx.HTTPStatusError as e: + print(f"HTTP Error: {e.response.status_code}", file=sys.stderr) + print(e.response.text, file=sys.stderr) + sys.exit(1) + except json.JSONDecodeError as e: + print(f"Invalid JSON in --params: {e}", file=sys.stderr) + sys.exit(1) + except Exception as e: + print(f"Error: {e}", file=sys.stderr) + sys.exit(1) + + +if __name__ == "__main__": + asyncio.run(main())