@cryptoquant_official/mcp 0.0.4 → 0.0.6-alpha
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.
- package/LICENSE +21 -0
- package/README.md +330 -37
- package/dist/ai-sdk/anthropic.d.ts +125 -0
- package/dist/ai-sdk/anthropic.d.ts.map +1 -0
- package/dist/ai-sdk/anthropic.js +278 -0
- package/dist/ai-sdk/anthropic.js.map +1 -0
- package/dist/ai-sdk/execute.d.ts +35 -0
- package/dist/ai-sdk/execute.d.ts.map +1 -0
- package/dist/ai-sdk/execute.js +531 -0
- package/dist/ai-sdk/execute.js.map +1 -0
- package/dist/ai-sdk/index.d.ts +53 -0
- package/dist/ai-sdk/index.d.ts.map +1 -0
- package/dist/ai-sdk/index.js +56 -0
- package/dist/ai-sdk/index.js.map +1 -0
- package/dist/ai-sdk/prompts.d.ts +34 -0
- package/dist/ai-sdk/prompts.d.ts.map +1 -0
- package/dist/ai-sdk/prompts.js +74 -0
- package/dist/ai-sdk/prompts.js.map +1 -0
- package/dist/ai-sdk/schemas.d.ts +66 -0
- package/dist/ai-sdk/schemas.d.ts.map +1 -0
- package/dist/ai-sdk/schemas.js +136 -0
- package/dist/ai-sdk/schemas.js.map +1 -0
- package/dist/ai-sdk/tools.d.ts +165 -0
- package/dist/ai-sdk/tools.d.ts.map +1 -0
- package/dist/ai-sdk/tools.js +152 -0
- package/dist/ai-sdk/tools.js.map +1 -0
- package/dist/ai-sdk/types.d.ts +149 -0
- package/dist/ai-sdk/types.d.ts.map +1 -0
- package/dist/ai-sdk/types.js +5 -0
- package/dist/ai-sdk/types.js.map +1 -0
- package/dist/core/auth/storage.d.ts.map +1 -0
- package/dist/core/auth/storage.js.map +1 -0
- package/dist/core/cache/storage.d.ts +110 -0
- package/dist/core/cache/storage.d.ts.map +1 -0
- package/dist/core/cache/storage.js +356 -0
- package/dist/core/cache/storage.js.map +1 -0
- package/dist/core/cache/summary.d.ts.map +1 -0
- package/dist/core/cache/summary.js.map +1 -0
- package/dist/{cache → core/cache}/types.d.ts +25 -0
- package/dist/{cache → core/cache}/types.d.ts.map +1 -1
- package/dist/core/cache/types.js.map +1 -0
- package/dist/core/config.d.ts.map +1 -0
- package/dist/core/config.js.map +1 -0
- package/dist/core/discovery.d.ts.map +1 -0
- package/dist/{discovery.js → core/discovery.js} +14 -2
- package/dist/core/discovery.js.map +1 -0
- package/dist/core/index.d.ts +16 -0
- package/dist/core/index.d.ts.map +1 -0
- package/dist/core/index.js +19 -0
- package/dist/core/index.js.map +1 -0
- package/dist/{permissions.d.ts → core/permissions.d.ts} +2 -2
- package/dist/core/permissions.d.ts.map +1 -0
- package/dist/{permissions.js → core/permissions.js} +38 -14
- package/dist/core/permissions.js.map +1 -0
- package/dist/core/plan-limits.d.ts.map +1 -0
- package/dist/core/plan-limits.js.map +1 -0
- package/dist/{utils.d.ts → core/utils.d.ts} +13 -0
- package/dist/core/utils.d.ts.map +1 -0
- package/dist/{utils.js → core/utils.js} +34 -0
- package/dist/core/utils.js.map +1 -0
- package/dist/data/metrics.toon +8 -5
- package/dist/http/chat-proxy.d.ts +32 -0
- package/dist/http/chat-proxy.d.ts.map +1 -0
- package/dist/http/chat-proxy.js +310 -0
- package/dist/http/chat-proxy.js.map +1 -0
- package/dist/http/index.d.ts +12 -0
- package/dist/http/index.d.ts.map +1 -0
- package/dist/http/index.js +30 -0
- package/dist/http/index.js.map +1 -0
- package/dist/http/server.d.ts +20 -0
- package/dist/http/server.d.ts.map +1 -0
- package/dist/http/server.js +231 -0
- package/dist/http/server.js.map +1 -0
- package/dist/shared/metrics-data.generated.d.ts +176 -0
- package/dist/shared/metrics-data.generated.d.ts.map +1 -0
- package/dist/shared/metrics-data.generated.js +3077 -0
- package/dist/shared/metrics-data.generated.js.map +1 -0
- package/dist/stdio/index.d.ts +10 -0
- package/dist/stdio/index.d.ts.map +1 -0
- package/dist/stdio/index.js +30 -0
- package/dist/stdio/index.js.map +1 -0
- package/dist/stdio/tools/auth.d.ts +3 -0
- package/dist/stdio/tools/auth.d.ts.map +1 -0
- package/dist/{tools → stdio/tools}/auth.js +38 -12
- package/dist/stdio/tools/auth.js.map +1 -0
- package/dist/stdio/tools/core.d.ts +3 -0
- package/dist/stdio/tools/core.d.ts.map +1 -0
- package/dist/{tools → stdio/tools}/core.js +122 -162
- package/dist/stdio/tools/core.js.map +1 -0
- package/package.json +55 -14
- package/dist/auth/storage.d.ts.map +0 -1
- package/dist/auth/storage.js.map +0 -1
- package/dist/cache/storage.d.ts +0 -47
- package/dist/cache/storage.d.ts.map +0 -1
- package/dist/cache/storage.js +0 -140
- package/dist/cache/storage.js.map +0 -1
- package/dist/cache/summary.d.ts.map +0 -1
- package/dist/cache/summary.js.map +0 -1
- package/dist/cache/types.js.map +0 -1
- package/dist/config.d.ts.map +0 -1
- package/dist/config.js.map +0 -1
- package/dist/discovery.d.ts.map +0 -1
- package/dist/discovery.js.map +0 -1
- package/dist/index.d.ts +0 -18
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js +0 -46
- package/dist/index.js.map +0 -1
- package/dist/permissions.d.ts.map +0 -1
- package/dist/permissions.js.map +0 -1
- package/dist/plan-limits.d.ts.map +0 -1
- package/dist/plan-limits.js.map +0 -1
- package/dist/tools/auth.d.ts +0 -3
- package/dist/tools/auth.d.ts.map +0 -1
- package/dist/tools/auth.js.map +0 -1
- package/dist/tools/core.d.ts +0 -3
- package/dist/tools/core.d.ts.map +0 -1
- package/dist/tools/core.js.map +0 -1
- package/dist/utils.d.ts.map +0 -1
- package/dist/utils.js.map +0 -1
- /package/dist/{auth → core/auth}/storage.d.ts +0 -0
- /package/dist/{auth → core/auth}/storage.js +0 -0
- /package/dist/{cache → core/cache}/summary.d.ts +0 -0
- /package/dist/{cache → core/cache}/summary.js +0 -0
- /package/dist/{cache → core/cache}/types.js +0 -0
- /package/dist/{config.d.ts → core/config.d.ts} +0 -0
- /package/dist/{config.js → core/config.js} +0 -0
- /package/dist/{discovery.d.ts → core/discovery.d.ts} +0 -0
- /package/dist/{plan-limits.d.ts → core/plan-limits.d.ts} +0 -0
- /package/dist/{plan-limits.js → core/plan-limits.js} +0 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 CryptoQuant
|
|
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.
|
package/README.md
CHANGED
|
@@ -1,20 +1,35 @@
|
|
|
1
1
|
# @cryptoquant_official/mcp
|
|
2
2
|
|
|
3
|
-
CryptoQuant
|
|
3
|
+
CryptoQuant on-chain analytics for Claude and AI applications.
|
|
4
4
|
|
|
5
|
-
##
|
|
5
|
+
## Integration Modes
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
| Mode | Use Case | Streaming | Setup |
|
|
8
|
+
|------|----------|-----------|-------|
|
|
9
|
+
| [**MCP**](#mode-1-mcp-claude-desktopcode) | Claude Desktop, Claude Code, Cursor | - | Config file |
|
|
10
|
+
| [**AI SDK**](#mode-2-ai-sdk-nextjs) | Next.js web apps | ✅ | npm package |
|
|
11
|
+
| [**HTTP**](#mode-3-http-chat-proxy) | Any backend / non-Node.js | ❌ | Standalone server |
|
|
8
12
|
|
|
9
|
-
|
|
13
|
+
**Get your API key**: [cryptoquant.com/settings/api](https://cryptoquant.com/settings/api)
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## Mode 1: MCP (Claude Desktop/Code)
|
|
18
|
+
|
|
19
|
+
For Claude Desktop, Claude Code, Cursor, and other MCP-compatible apps.
|
|
10
20
|
|
|
11
|
-
|
|
21
|
+
### Setup
|
|
22
|
+
|
|
23
|
+
**Claude Code** (`~/.claude/mcp.json` or `.mcp.json`):
|
|
12
24
|
```json
|
|
13
25
|
{
|
|
14
26
|
"mcpServers": {
|
|
15
27
|
"cryptoquant": {
|
|
16
28
|
"command": "npx",
|
|
17
|
-
"args": ["-y", "@cryptoquant_official/mcp"]
|
|
29
|
+
"args": ["-y", "@cryptoquant_official/mcp"],
|
|
30
|
+
"env": {
|
|
31
|
+
"CRYPTOQUANT_API_KEY": "your-api-key"
|
|
32
|
+
}
|
|
18
33
|
}
|
|
19
34
|
}
|
|
20
35
|
}
|
|
@@ -26,51 +41,307 @@ Add to your MCP configuration:
|
|
|
26
41
|
"mcpServers": {
|
|
27
42
|
"cryptoquant": {
|
|
28
43
|
"command": "npx",
|
|
29
|
-
"args": ["-y", "@cryptoquant_official/mcp"]
|
|
44
|
+
"args": ["-y", "@cryptoquant_official/mcp"],
|
|
45
|
+
"env": {
|
|
46
|
+
"CRYPTOQUANT_API_KEY": "your-api-key"
|
|
47
|
+
}
|
|
30
48
|
}
|
|
31
49
|
}
|
|
32
50
|
}
|
|
33
51
|
```
|
|
34
52
|
|
|
35
|
-
|
|
53
|
+
Restart your app after configuration.
|
|
54
|
+
|
|
55
|
+
### Usage
|
|
56
|
+
|
|
57
|
+
```
|
|
58
|
+
User: "Is Bitcoin overvalued?"
|
|
59
|
+
|
|
60
|
+
Claude: Let me check the MVRV ratio...
|
|
61
|
+
→ Calls query_data({ endpoint: "/v1/btc/market-data/mvrv" })
|
|
62
|
+
→ Returns analysis with current valuation status
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
---
|
|
36
66
|
|
|
37
|
-
|
|
67
|
+
## Mode 2: AI SDK (Next.js)
|
|
68
|
+
|
|
69
|
+
For Next.js web apps with streaming responses via Vercel AI SDK.
|
|
70
|
+
|
|
71
|
+
### Installation
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
npm install @cryptoquant_official/mcp ai @ai-sdk/react @ai-sdk/anthropic
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
### Environment Variables
|
|
78
|
+
|
|
79
|
+
```env
|
|
80
|
+
# .env.local
|
|
81
|
+
ANTHROPIC_API_KEY=sk-ant-...
|
|
82
|
+
CRYPTOQUANT_API_KEY=cq-...
|
|
83
|
+
```
|
|
38
84
|
|
|
39
|
-
|
|
85
|
+
> `@ai-sdk/anthropic` automatically reads `ANTHROPIC_API_KEY`.
|
|
40
86
|
|
|
41
|
-
|
|
87
|
+
### API Route
|
|
42
88
|
|
|
43
|
-
|
|
89
|
+
```typescript
|
|
90
|
+
// app/api/chat/route.ts
|
|
91
|
+
import { streamText } from "ai";
|
|
92
|
+
import { anthropic } from "@ai-sdk/anthropic";
|
|
93
|
+
import { createCryptoQuantTools } from "@cryptoquant_official/mcp/ai-sdk";
|
|
44
94
|
|
|
45
|
-
|
|
95
|
+
export const maxDuration = 60;
|
|
46
96
|
|
|
47
|
-
|
|
97
|
+
export async function POST(req: Request) {
|
|
98
|
+
const { messages } = await req.json();
|
|
48
99
|
|
|
100
|
+
const result = streamText({
|
|
101
|
+
model: anthropic("claude-sonnet-4-20250514"),
|
|
102
|
+
messages,
|
|
103
|
+
tools: createCryptoQuantTools({
|
|
104
|
+
cryptoquantApiKey: process.env.CRYPTOQUANT_API_KEY!,
|
|
105
|
+
}),
|
|
106
|
+
maxSteps: 5,
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
return result.toDataStreamResponse();
|
|
110
|
+
}
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
### Client Component
|
|
114
|
+
|
|
115
|
+
```tsx
|
|
116
|
+
"use client";
|
|
117
|
+
import { useChat } from "@ai-sdk/react";
|
|
118
|
+
|
|
119
|
+
export function CryptoChat() {
|
|
120
|
+
const { messages, input, handleInputChange, handleSubmit, isLoading } = useChat();
|
|
121
|
+
|
|
122
|
+
return (
|
|
123
|
+
<div>
|
|
124
|
+
{messages.map((m) => (
|
|
125
|
+
<div key={m.id}>{m.content}</div>
|
|
126
|
+
))}
|
|
127
|
+
<form onSubmit={handleSubmit}>
|
|
128
|
+
<input value={input} onChange={handleInputChange} />
|
|
129
|
+
<button type="submit" disabled={isLoading}>Send</button>
|
|
130
|
+
</form>
|
|
131
|
+
</div>
|
|
132
|
+
);
|
|
133
|
+
}
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
### API Reference
|
|
137
|
+
|
|
138
|
+
```typescript
|
|
139
|
+
// Option 1: With explicit API key
|
|
140
|
+
import { createCryptoQuantTools } from "@cryptoquant_official/mcp/ai-sdk";
|
|
141
|
+
const tools = createCryptoQuantTools({ cryptoquantApiKey: "cq-..." });
|
|
142
|
+
|
|
143
|
+
// Option 2: Using CRYPTOQUANT_API_KEY env var
|
|
144
|
+
import { cryptoQuantTools } from "@cryptoquant_official/mcp/ai-sdk";
|
|
145
|
+
streamText({ tools: cryptoQuantTools, ... });
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
See [examples/nextjs-ai-sdk/](examples/nextjs-ai-sdk/) for complete example.
|
|
149
|
+
|
|
150
|
+
---
|
|
151
|
+
|
|
152
|
+
## Mode 3: HTTP (Chat Proxy)
|
|
153
|
+
|
|
154
|
+
Standalone HTTP server for any backend or non-Node.js environments.
|
|
155
|
+
|
|
156
|
+
### Start Server
|
|
157
|
+
|
|
158
|
+
```bash
|
|
159
|
+
# Using npx
|
|
160
|
+
npx @cryptoquant_official/mcp/http
|
|
161
|
+
|
|
162
|
+
# Or if installed locally
|
|
163
|
+
node dist/http/index.js
|
|
164
|
+
|
|
165
|
+
# Server runs at http://localhost:3100
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
### Endpoints
|
|
169
|
+
|
|
170
|
+
| Endpoint | Method | Description |
|
|
171
|
+
|----------|--------|-------------|
|
|
172
|
+
| `/chat` | POST | Chat with Claude + automatic tool execution |
|
|
173
|
+
| `/tools/schema` | GET | Get Claude API tool definitions |
|
|
174
|
+
| `/health` | GET | Server health check |
|
|
175
|
+
| `/mcp` | POST/GET/DELETE | Streamable HTTP MCP protocol |
|
|
176
|
+
|
|
177
|
+
### POST /chat
|
|
178
|
+
|
|
179
|
+
```bash
|
|
180
|
+
curl -X POST http://localhost:3100/chat \
|
|
181
|
+
-H "Content-Type: application/json" \
|
|
182
|
+
-d '{
|
|
183
|
+
"message": "Analyze Bitcoin MVRV",
|
|
184
|
+
"claude_api_key": "sk-ant-...",
|
|
185
|
+
"cryptoquant_api_key": "cq-..."
|
|
186
|
+
}'
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
**Request:**
|
|
49
190
|
```json
|
|
50
191
|
{
|
|
51
|
-
"
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
192
|
+
"message": "Is Bitcoin overvalued?",
|
|
193
|
+
"claude_api_key": "sk-ant-...",
|
|
194
|
+
"cryptoquant_api_key": "cq-...",
|
|
195
|
+
"conversation_id": "optional-session-id",
|
|
196
|
+
"model": "claude-sonnet-4-20250514"
|
|
197
|
+
}
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
**Response:**
|
|
201
|
+
```json
|
|
202
|
+
{
|
|
203
|
+
"success": true,
|
|
204
|
+
"response": "Based on the current MVRV of 2.1...",
|
|
205
|
+
"tool_calls": [
|
|
206
|
+
{ "name": "initialize", "result": { "success": true } },
|
|
207
|
+
{ "name": "query_data", "result": { "data": [...] } }
|
|
208
|
+
],
|
|
209
|
+
"conversation_id": "abc-123",
|
|
210
|
+
"usage": { "input_tokens": 150, "output_tokens": 300 }
|
|
211
|
+
}
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
### Next.js with HTTP Mode
|
|
215
|
+
|
|
216
|
+
```typescript
|
|
217
|
+
// app/api/chat/route.ts
|
|
218
|
+
export async function POST(request: Request) {
|
|
219
|
+
const { message, conversation_id } = await request.json();
|
|
220
|
+
|
|
221
|
+
const response = await fetch("http://localhost:3100/chat", {
|
|
222
|
+
method: "POST",
|
|
223
|
+
headers: { "Content-Type": "application/json" },
|
|
224
|
+
body: JSON.stringify({
|
|
225
|
+
message,
|
|
226
|
+
claude_api_key: process.env.CLAUDE_API_KEY,
|
|
227
|
+
cryptoquant_api_key: process.env.CRYPTOQUANT_API_KEY,
|
|
228
|
+
conversation_id,
|
|
229
|
+
}),
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
return Response.json(await response.json());
|
|
233
|
+
}
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
See [examples/nextjs/](examples/nextjs/) for complete example.
|
|
237
|
+
|
|
238
|
+
---
|
|
239
|
+
|
|
240
|
+
## Custom Cache Storage
|
|
241
|
+
|
|
242
|
+
The MCP server caches API discovery data to reduce startup time. By default:
|
|
243
|
+
- **CLI/Local**: File storage (`~/.cryptoquant/discovery-cache.json`)
|
|
244
|
+
- **Web Server**: No caching (requires Redis configuration)
|
|
245
|
+
|
|
246
|
+
### Redis Storage for Web Servers
|
|
247
|
+
|
|
248
|
+
For production web servers or serverless environments, configure Redis storage:
|
|
249
|
+
|
|
250
|
+
```typescript
|
|
251
|
+
// app/lib/cryptoquant-cache.ts
|
|
252
|
+
import { createClient } from "redis";
|
|
253
|
+
import { setRedisStorage, type CacheStorage } from "@cryptoquant_official/mcp";
|
|
254
|
+
|
|
255
|
+
const redis = createClient({ url: process.env.REDIS_URL });
|
|
256
|
+
|
|
257
|
+
export async function configureRedisCache() {
|
|
258
|
+
if (!redis.isOpen) {
|
|
259
|
+
await redis.connect();
|
|
59
260
|
}
|
|
261
|
+
|
|
262
|
+
const redisStorage: CacheStorage = {
|
|
263
|
+
type: "redis",
|
|
264
|
+
|
|
265
|
+
async read(apiUrl, apiKeyPrefix) {
|
|
266
|
+
const key = `cryptoquant:cache:${apiUrl}:${apiKeyPrefix}`;
|
|
267
|
+
const data = await redis.get(key);
|
|
268
|
+
return data ? JSON.parse(data) : null;
|
|
269
|
+
},
|
|
270
|
+
|
|
271
|
+
async write(apiUrl, cache) {
|
|
272
|
+
const key = `cryptoquant:cache:${apiUrl}:${cache.metadata.api_key_prefix}`;
|
|
273
|
+
const ttl = 7 * 24 * 60 * 60; // 7 days
|
|
274
|
+
await redis.setEx(key, ttl, JSON.stringify(cache));
|
|
275
|
+
},
|
|
276
|
+
|
|
277
|
+
async invalidate(apiUrl, apiKeyPrefix) {
|
|
278
|
+
if (apiKeyPrefix) {
|
|
279
|
+
await redis.del(`cryptoquant:cache:${apiUrl}:${apiKeyPrefix}`);
|
|
280
|
+
} else {
|
|
281
|
+
const keys = await redis.keys(`cryptoquant:cache:${apiUrl}:*`);
|
|
282
|
+
if (keys.length > 0) await redis.del(keys);
|
|
283
|
+
}
|
|
284
|
+
},
|
|
285
|
+
|
|
286
|
+
async clearAll() {
|
|
287
|
+
const keys = await redis.keys("cryptoquant:cache:*");
|
|
288
|
+
if (keys.length > 0) await redis.del(keys);
|
|
289
|
+
},
|
|
290
|
+
|
|
291
|
+
getPath() {
|
|
292
|
+
return "[redis]";
|
|
293
|
+
},
|
|
294
|
+
};
|
|
295
|
+
|
|
296
|
+
setRedisStorage(redisStorage);
|
|
60
297
|
}
|
|
61
298
|
```
|
|
62
299
|
|
|
63
|
-
|
|
300
|
+
```typescript
|
|
301
|
+
// app/api/chat/route.ts
|
|
302
|
+
import { configureRedisCache } from "@/lib/cryptoquant-cache";
|
|
64
303
|
|
|
65
|
-
|
|
304
|
+
// Configure once at startup
|
|
305
|
+
await configureRedisCache();
|
|
66
306
|
|
|
307
|
+
export async function POST(req: Request) {
|
|
308
|
+
// ... your chat handler
|
|
309
|
+
}
|
|
67
310
|
```
|
|
68
|
-
|
|
311
|
+
|
|
312
|
+
### CacheStorage Interface
|
|
313
|
+
|
|
314
|
+
Implement this interface for custom storage backends:
|
|
315
|
+
|
|
316
|
+
```typescript
|
|
317
|
+
interface CacheStorage {
|
|
318
|
+
/** Storage type identifier */
|
|
319
|
+
readonly type: "file" | "redis" | "auto";
|
|
320
|
+
|
|
321
|
+
/** Read cache data, returns null if not found */
|
|
322
|
+
read(apiUrl: string, apiKeyPrefix?: string): Promise<DiscoveryCacheSchema | null> | DiscoveryCacheSchema | null;
|
|
323
|
+
|
|
324
|
+
/** Write cache data */
|
|
325
|
+
write(apiUrl: string, cache: DiscoveryCacheSchema): Promise<void> | void;
|
|
326
|
+
|
|
327
|
+
/** Delete cache for specific API URL */
|
|
328
|
+
invalidate(apiUrl: string, apiKeyPrefix?: string): Promise<void> | void;
|
|
329
|
+
|
|
330
|
+
/** Clear all caches */
|
|
331
|
+
clearAll(): Promise<void> | void;
|
|
332
|
+
|
|
333
|
+
/** Get storage path for display */
|
|
334
|
+
getPath(apiUrl: string): string;
|
|
335
|
+
}
|
|
69
336
|
```
|
|
70
337
|
|
|
71
|
-
|
|
338
|
+
See [examples/redis-cache/](examples/redis-cache/) for complete implementation.
|
|
339
|
+
|
|
340
|
+
---
|
|
72
341
|
|
|
73
|
-
##
|
|
342
|
+
## Reference
|
|
343
|
+
|
|
344
|
+
### Available Tools
|
|
74
345
|
|
|
75
346
|
| Tool | Description |
|
|
76
347
|
|------|-------------|
|
|
@@ -79,31 +350,53 @@ initialize(api_key='your-api-key')
|
|
|
79
350
|
| `get_endpoint_info` | Get endpoint parameter details |
|
|
80
351
|
| `query_data` | Query raw on-chain data |
|
|
81
352
|
| `describe_metric` | Get metric descriptions and thresholds |
|
|
82
|
-
| `list_assets` | List supported assets
|
|
353
|
+
| `list_assets` | List supported assets |
|
|
83
354
|
| `reset_session` | Clear session / switch accounts |
|
|
84
355
|
|
|
85
|
-
|
|
356
|
+
### Supported Assets
|
|
86
357
|
|
|
87
358
|
BTC, ETH, ALT, Stablecoin, ERC20, TRX, XRP
|
|
88
359
|
|
|
89
|
-
|
|
360
|
+
### Environment Variables
|
|
90
361
|
|
|
91
|
-
|
|
92
|
-
|
|
362
|
+
| Variable | Description | Default |
|
|
363
|
+
|----------|-------------|---------|
|
|
364
|
+
| `CRYPTOQUANT_API_KEY` | CryptoQuant API key | - |
|
|
365
|
+
| `ANTHROPIC_API_KEY` | Anthropic API key (AI SDK mode) | - |
|
|
366
|
+
| `MCP_HTTP_PORT` | HTTP server port | `3100` |
|
|
367
|
+
| `MCP_HTTP_HOST` | HTTP server host | `127.0.0.1` |
|
|
368
|
+
| `MCP_CORS_ORIGINS` | CORS allowed origins (comma-separated) | `http://localhost:3000,http://localhost:5173` |
|
|
369
|
+
| `MCP_ENABLE_LEGACY_SSE` | Enable legacy SSE transport | `false` |
|
|
370
|
+
| `DEBUG` | Enable debug logging | - |
|
|
93
371
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
372
|
+
### HTTP Security Configuration
|
|
373
|
+
|
|
374
|
+
The HTTP server includes several security features enabled by default:
|
|
375
|
+
|
|
376
|
+
| Feature | Description | Configuration |
|
|
377
|
+
|---------|-------------|---------------|
|
|
378
|
+
| **Local binding** | Server binds to localhost only | Set `MCP_HTTP_HOST=0.0.0.0` for external access |
|
|
379
|
+
| **CORS restriction** | Only localhost origins allowed | Set `MCP_CORS_ORIGINS` for production domains |
|
|
380
|
+
| **Rate limiting** | 100 req/15min (general), 30 req/15min (/chat) | Built-in, no configuration needed |
|
|
381
|
+
| **Minimal health endpoint** | `/health` returns only `{ status: "ok" }` | No sensitive info exposed |
|
|
382
|
+
|
|
383
|
+
**Production deployment example:**
|
|
384
|
+
|
|
385
|
+
```bash
|
|
386
|
+
export MCP_HTTP_HOST=0.0.0.0
|
|
387
|
+
export MCP_CORS_ORIGINS=https://myapp.com,https://api.myapp.com
|
|
388
|
+
npx @cryptoquant_official/mcp/http
|
|
97
389
|
```
|
|
98
390
|
|
|
99
|
-
|
|
391
|
+
### Requirements
|
|
100
392
|
|
|
101
393
|
- Node.js 18+
|
|
102
394
|
- CryptoQuant API key
|
|
395
|
+
- Anthropic API key (for AI SDK / HTTP mode)
|
|
103
396
|
|
|
104
397
|
## Links
|
|
105
398
|
|
|
106
|
-
- [
|
|
399
|
+
- [GitHub](https://github.com/CryptoQuantOfficial/cryptoquant-skills)
|
|
107
400
|
- [CryptoQuant](https://cryptoquant.com)
|
|
108
401
|
- [API Docs](https://docs.cryptoquant.com)
|
|
109
402
|
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Anthropic SDK Integration for CryptoQuant Tools
|
|
3
|
+
*
|
|
4
|
+
* Provides CryptoQuant tools in native Anthropic SDK format for direct use
|
|
5
|
+
* with @anthropic-ai/sdk's client.messages.create().
|
|
6
|
+
*
|
|
7
|
+
* @packageDocumentation
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* ```typescript
|
|
11
|
+
* import Anthropic from '@anthropic-ai/sdk';
|
|
12
|
+
* import { createAnthropicTools, createToolExecutor } from '@cryptoquant_official/mcp/ai-sdk';
|
|
13
|
+
*
|
|
14
|
+
* const client = new Anthropic({ apiKey: process.env.CLAUDE_API_KEY });
|
|
15
|
+
* const toolDefinitions = createAnthropicTools();
|
|
16
|
+
* const executeTool = createToolExecutor({ cryptoquantApiKey: process.env.CRYPTOQUANT_API_KEY });
|
|
17
|
+
*
|
|
18
|
+
* const response = await client.messages.create({
|
|
19
|
+
* model: 'claude-sonnet-4-20250514',
|
|
20
|
+
* max_tokens: 4096,
|
|
21
|
+
* tools: toolDefinitions,
|
|
22
|
+
* messages: [{ role: 'user', content: 'What is MVRV?' }],
|
|
23
|
+
* });
|
|
24
|
+
*
|
|
25
|
+
* // Handle tool calls
|
|
26
|
+
* for (const block of response.content) {
|
|
27
|
+
* if (block.type === 'tool_use') {
|
|
28
|
+
* const result = await executeTool(block.name, block.input);
|
|
29
|
+
* // ... use result
|
|
30
|
+
* }
|
|
31
|
+
* }
|
|
32
|
+
* ```
|
|
33
|
+
*/
|
|
34
|
+
import type { ToolExecutionContext } from "./types.js";
|
|
35
|
+
/**
|
|
36
|
+
* Tool names available in CryptoQuant
|
|
37
|
+
*/
|
|
38
|
+
export type CryptoQuantToolName = "initialize" | "discover_endpoints" | "query_data" | "describe_metric" | "market_summary" | "trading_signal" | "whale_activity";
|
|
39
|
+
/**
|
|
40
|
+
* Anthropic Tool definition interface (matches @anthropic-ai/sdk types)
|
|
41
|
+
* Defined here to avoid requiring @anthropic-ai/sdk as a dependency
|
|
42
|
+
*/
|
|
43
|
+
export interface AnthropicTool {
|
|
44
|
+
name: string;
|
|
45
|
+
description: string;
|
|
46
|
+
input_schema: {
|
|
47
|
+
type: "object";
|
|
48
|
+
properties: Record<string, unknown>;
|
|
49
|
+
required: string[];
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Cache TTL recommendations for each tool (in seconds)
|
|
54
|
+
* Consumers can use this to implement caching strategies
|
|
55
|
+
*/
|
|
56
|
+
export declare const toolCacheTTL: Record<CryptoQuantToolName, number>;
|
|
57
|
+
/**
|
|
58
|
+
* Create CryptoQuant tool definitions in Anthropic SDK format
|
|
59
|
+
*
|
|
60
|
+
* Returns an array of tool definitions compatible with @anthropic-ai/sdk's
|
|
61
|
+
* client.messages.create() tools parameter.
|
|
62
|
+
*
|
|
63
|
+
* @returns Array of Anthropic-compatible tool definitions
|
|
64
|
+
*
|
|
65
|
+
* @example
|
|
66
|
+
* ```typescript
|
|
67
|
+
* import Anthropic from '@anthropic-ai/sdk';
|
|
68
|
+
* import { createAnthropicTools } from '@cryptoquant_official/mcp/ai-sdk';
|
|
69
|
+
*
|
|
70
|
+
* const client = new Anthropic({ apiKey: process.env.CLAUDE_API_KEY });
|
|
71
|
+
*
|
|
72
|
+
* const response = await client.messages.create({
|
|
73
|
+
* model: 'claude-sonnet-4-20250514',
|
|
74
|
+
* max_tokens: 4096,
|
|
75
|
+
* tools: createAnthropicTools(),
|
|
76
|
+
* messages: [{ role: 'user', content: 'Analyze Bitcoin market' }],
|
|
77
|
+
* });
|
|
78
|
+
* ```
|
|
79
|
+
*/
|
|
80
|
+
export declare function createAnthropicTools(): AnthropicTool[];
|
|
81
|
+
/**
|
|
82
|
+
* Type for the tool executor function
|
|
83
|
+
*/
|
|
84
|
+
export type ToolExecutorFn = (name: string, input: Record<string, unknown>) => Promise<unknown>;
|
|
85
|
+
/**
|
|
86
|
+
* Create a tool executor function for handling tool calls
|
|
87
|
+
*
|
|
88
|
+
* Returns a function that executes CryptoQuant tools by name.
|
|
89
|
+
* Handles all type casting internally - consumers just pass the raw input.
|
|
90
|
+
*
|
|
91
|
+
* @param context - Execution context with API key
|
|
92
|
+
* @returns Async function that executes tools by name
|
|
93
|
+
*
|
|
94
|
+
* @example
|
|
95
|
+
* ```typescript
|
|
96
|
+
* import { createToolExecutor } from '@cryptoquant_official/mcp/ai-sdk';
|
|
97
|
+
*
|
|
98
|
+
* const executeTool = createToolExecutor({
|
|
99
|
+
* cryptoquantApiKey: process.env.CRYPTOQUANT_API_KEY,
|
|
100
|
+
* });
|
|
101
|
+
*
|
|
102
|
+
* // In your agentic loop:
|
|
103
|
+
* for (const block of response.content) {
|
|
104
|
+
* if (block.type === 'tool_use') {
|
|
105
|
+
* const result = await executeTool(block.name, block.input);
|
|
106
|
+
* toolResults.push({
|
|
107
|
+
* type: 'tool_result',
|
|
108
|
+
* tool_use_id: block.id,
|
|
109
|
+
* content: JSON.stringify(result),
|
|
110
|
+
* });
|
|
111
|
+
* }
|
|
112
|
+
* }
|
|
113
|
+
* ```
|
|
114
|
+
*/
|
|
115
|
+
export declare function createToolExecutor(context: ToolExecutionContext): ToolExecutorFn;
|
|
116
|
+
/**
|
|
117
|
+
* Get tool names as an array
|
|
118
|
+
* Useful for validation or iteration
|
|
119
|
+
*/
|
|
120
|
+
export declare function getToolNames(): CryptoQuantToolName[];
|
|
121
|
+
/**
|
|
122
|
+
* Check if a string is a valid CryptoQuant tool name
|
|
123
|
+
*/
|
|
124
|
+
export declare function isValidToolName(name: string): name is CryptoQuantToolName;
|
|
125
|
+
//# sourceMappingURL=anthropic.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"anthropic.d.ts","sourceRoot":"","sources":["../../src/ai-sdk/anthropic.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AAoBH,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AAEvD;;GAEG;AACH,MAAM,MAAM,mBAAmB,GAC3B,YAAY,GACZ,oBAAoB,GACpB,YAAY,GACZ,iBAAiB,GACjB,gBAAgB,GAChB,gBAAgB,GAChB,gBAAgB,CAAC;AAErB;;;GAGG;AACH,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE;QACZ,IAAI,EAAE,QAAQ,CAAC;QACf,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;QACpC,QAAQ,EAAE,MAAM,EAAE,CAAC;KACpB,CAAC;CACH;AAED;;;GAGG;AACH,eAAO,MAAM,YAAY,EAAE,MAAM,CAAC,mBAAmB,EAAE,MAAM,CAQ5D,CAAC;AAuIF;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,oBAAoB,IAAI,aAAa,EAAE,CAgBtD;AAED;;GAEG;AACH,MAAM,MAAM,cAAc,GAAG,CAC3B,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAC3B,OAAO,CAAC,OAAO,CAAC,CAAC;AAEtB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,wBAAgB,kBAAkB,CAChC,OAAO,EAAE,oBAAoB,GAC5B,cAAc,CA+BhB;AAED;;;GAGG;AACH,wBAAgB,YAAY,IAAI,mBAAmB,EAAE,CAUpD;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,IAAI,mBAAmB,CAEzE"}
|