@frase/mcp-server 0.1.0
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/CHANGELOG.md +44 -0
- package/README.md +262 -0
- package/dist/api-client.d.ts +93 -0
- package/dist/api-client.d.ts.map +1 -0
- package/dist/api-client.js +213 -0
- package/dist/api-client.js.map +1 -0
- package/dist/cache.d.ts +52 -0
- package/dist/cache.d.ts.map +1 -0
- package/dist/cache.js +97 -0
- package/dist/cache.js.map +1 -0
- package/dist/config.d.ts +17 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +31 -0
- package/dist/config.js.map +1 -0
- package/dist/formatter.d.ts +47 -0
- package/dist/formatter.d.ts.map +1 -0
- package/dist/formatter.js +136 -0
- package/dist/formatter.js.map +1 -0
- package/dist/index.d.ts +32 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +292 -0
- package/dist/index.js.map +1 -0
- package/dist/prompts/competitor-analysis.d.ts +18 -0
- package/dist/prompts/competitor-analysis.d.ts.map +1 -0
- package/dist/prompts/competitor-analysis.js +71 -0
- package/dist/prompts/competitor-analysis.js.map +1 -0
- package/dist/prompts/content-audit.d.ts +18 -0
- package/dist/prompts/content-audit.d.ts.map +1 -0
- package/dist/prompts/content-audit.js +67 -0
- package/dist/prompts/content-audit.js.map +1 -0
- package/dist/prompts/create-seo-article.d.ts +19 -0
- package/dist/prompts/create-seo-article.d.ts.map +1 -0
- package/dist/prompts/create-seo-article.js +78 -0
- package/dist/prompts/create-seo-article.js.map +1 -0
- package/dist/prompts/index.d.ts +18 -0
- package/dist/prompts/index.d.ts.map +1 -0
- package/dist/prompts/index.js +52 -0
- package/dist/prompts/index.js.map +1 -0
- package/dist/prompts/keyword-research.d.ts +18 -0
- package/dist/prompts/keyword-research.d.ts.map +1 -0
- package/dist/prompts/keyword-research.js +72 -0
- package/dist/prompts/keyword-research.js.map +1 -0
- package/dist/prompts/optimize-content.d.ts +18 -0
- package/dist/prompts/optimize-content.d.ts.map +1 -0
- package/dist/prompts/optimize-content.js +59 -0
- package/dist/prompts/optimize-content.js.map +1 -0
- package/dist/resources/briefs.d.ts +26 -0
- package/dist/resources/briefs.d.ts.map +1 -0
- package/dist/resources/briefs.js +144 -0
- package/dist/resources/briefs.js.map +1 -0
- package/dist/resources/content.d.ts +26 -0
- package/dist/resources/content.d.ts.map +1 -0
- package/dist/resources/content.js +128 -0
- package/dist/resources/content.js.map +1 -0
- package/dist/resources/index.d.ts +32 -0
- package/dist/resources/index.d.ts.map +1 -0
- package/dist/resources/index.js +85 -0
- package/dist/resources/index.js.map +1 -0
- package/dist/resources/sites.d.ts +26 -0
- package/dist/resources/sites.d.ts.map +1 -0
- package/dist/resources/sites.js +108 -0
- package/dist/resources/sites.js.map +1 -0
- package/dist/tools/ai-visibility.d.ts +25 -0
- package/dist/tools/ai-visibility.d.ts.map +1 -0
- package/dist/tools/ai-visibility.js +537 -0
- package/dist/tools/ai-visibility.js.map +1 -0
- package/dist/tools/analytics.d.ts +17 -0
- package/dist/tools/analytics.d.ts.map +1 -0
- package/dist/tools/analytics.js +311 -0
- package/dist/tools/analytics.js.map +1 -0
- package/dist/tools/audits.d.ts +73 -0
- package/dist/tools/audits.d.ts.map +1 -0
- package/dist/tools/audits.js +345 -0
- package/dist/tools/audits.js.map +1 -0
- package/dist/tools/briefs.d.ts +63 -0
- package/dist/tools/briefs.d.ts.map +1 -0
- package/dist/tools/briefs.js +276 -0
- package/dist/tools/briefs.js.map +1 -0
- package/dist/tools/content.d.ts +51 -0
- package/dist/tools/content.d.ts.map +1 -0
- package/dist/tools/content.js +233 -0
- package/dist/tools/content.js.map +1 -0
- package/dist/tools/index.d.ts +29 -0
- package/dist/tools/index.d.ts.map +1 -0
- package/dist/tools/index.js +96 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/tools/jobs.d.ts +22 -0
- package/dist/tools/jobs.d.ts.map +1 -0
- package/dist/tools/jobs.js +124 -0
- package/dist/tools/jobs.js.map +1 -0
- package/dist/tools/optimizations.d.ts +19 -0
- package/dist/tools/optimizations.d.ts.map +1 -0
- package/dist/tools/optimizations.js +339 -0
- package/dist/tools/optimizations.js.map +1 -0
- package/dist/tools/research.d.ts +41 -0
- package/dist/tools/research.d.ts.map +1 -0
- package/dist/tools/research.js +151 -0
- package/dist/tools/research.js.map +1 -0
- package/dist/tools/serp.d.ts +15 -0
- package/dist/tools/serp.d.ts.map +1 -0
- package/dist/tools/serp.js +267 -0
- package/dist/tools/serp.js.map +1 -0
- package/dist/tools/sites.d.ts +31 -0
- package/dist/tools/sites.d.ts.map +1 -0
- package/dist/tools/sites.js +83 -0
- package/dist/tools/sites.js.map +1 -0
- package/dist/tools/webhooks.d.ts +19 -0
- package/dist/tools/webhooks.d.ts.map +1 -0
- package/dist/tools/webhooks.js +350 -0
- package/dist/tools/webhooks.js.map +1 -0
- package/dist/types.d.ts +167 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +5 -0
- package/dist/types.js.map +1 -0
- package/package.json +67 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [0.1.0] - 2026-02-01
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
#### Tools (38 total)
|
|
13
|
+
- **Sites**: `list_sites`
|
|
14
|
+
- **Briefs**: `list_briefs`, `create_brief`, `get_brief`
|
|
15
|
+
- **Content**: `list_content`, `get_content`
|
|
16
|
+
- **Research**: `list_research`, `start_research`
|
|
17
|
+
- **Jobs**: `get_job_status`
|
|
18
|
+
- **Audits**: `list_audits`, `start_audit`, `get_audit`, `export_audit`
|
|
19
|
+
- **SERP**: `analyze_serp`, `analyze_competitors`, `get_search_intent`
|
|
20
|
+
- **AI Visibility**: `get_ai_visibility`, `list_prompts`, `create_prompt`, `get_prompt`, `update_prompt`, `get_competitors`, `get_alerts`, `get_insights`
|
|
21
|
+
- **Analytics**: `get_gsc_overview`, `get_gsc_queries`, `get_gsc_pages`, `get_content_gaps`
|
|
22
|
+
- **Optimizations**: `list_optimizations`, `start_optimization`, `get_optimization`, `apply_optimization`, `get_optimization_insights`
|
|
23
|
+
- **Webhooks**: `list_webhooks`, `create_webhook`, `get_webhook`, `update_webhook`, `delete_webhook`
|
|
24
|
+
|
|
25
|
+
#### Resources
|
|
26
|
+
- `frase://sites` - List all sites
|
|
27
|
+
- `frase://sites/{id}` - Individual site details
|
|
28
|
+
- `frase://content` - List all content
|
|
29
|
+
- `frase://content/{id}` - Individual content with body
|
|
30
|
+
- `frase://briefs` - List all briefs
|
|
31
|
+
- `frase://briefs/{id}` - Individual brief with outline
|
|
32
|
+
|
|
33
|
+
#### Prompts
|
|
34
|
+
- `create_seo_article` - Full SEO article creation workflow
|
|
35
|
+
- `optimize_content` - Content optimization workflow
|
|
36
|
+
- `keyword_research` - Keyword research workflow
|
|
37
|
+
- `competitor_analysis` - Competitor analysis workflow
|
|
38
|
+
- `content_audit` - Site content audit workflow
|
|
39
|
+
|
|
40
|
+
#### Features
|
|
41
|
+
- API client with retry logic and exponential backoff
|
|
42
|
+
- Response caching (60s for lists, 5min for resources)
|
|
43
|
+
- Markdown-formatted responses for better readability
|
|
44
|
+
- Debug mode via `FRASE_MCP_DEBUG` environment variable
|
package/README.md
ADDED
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
# @frase/mcp-server
|
|
2
|
+
|
|
3
|
+
[](https://www.npmjs.com/package/@frase/mcp-server)
|
|
4
|
+
[](https://opensource.org/licenses/MIT)
|
|
5
|
+
|
|
6
|
+
Use Claude Desktop to interact with your Frase account. This MCP (Model Context Protocol) server exposes Frase's API as tools that Claude can use to help you with SEO content creation, research, and optimization.
|
|
7
|
+
|
|
8
|
+
## What is MCP?
|
|
9
|
+
|
|
10
|
+
[Model Context Protocol (MCP)](https://modelcontextprotocol.io/) is an open standard that allows AI assistants like Claude to securely connect to external tools and data sources. This server implements MCP to give Claude access to your Frase account.
|
|
11
|
+
|
|
12
|
+
## Quick Start
|
|
13
|
+
|
|
14
|
+
### 1. Get your API key
|
|
15
|
+
|
|
16
|
+
Get your API key from [Frase Settings](https://app.frase.io/settings/api).
|
|
17
|
+
|
|
18
|
+
### 2. Configure Claude Desktop
|
|
19
|
+
|
|
20
|
+
Add this to your Claude Desktop config file:
|
|
21
|
+
|
|
22
|
+
**macOS:** `~/Library/Application Support/Claude/claude_desktop_config.json`
|
|
23
|
+
**Windows:** `%APPDATA%\Claude\claude_desktop_config.json`
|
|
24
|
+
|
|
25
|
+
```json
|
|
26
|
+
{
|
|
27
|
+
"mcpServers": {
|
|
28
|
+
"frase": {
|
|
29
|
+
"command": "npx",
|
|
30
|
+
"args": ["-y", "@frase/mcp-server"],
|
|
31
|
+
"env": {
|
|
32
|
+
"FRASE_API_KEY": "sk_live_your_api_key_here"
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
### 3. Restart Claude Desktop
|
|
40
|
+
|
|
41
|
+
After saving the config, restart Claude Desktop. You should see "frase" in the MCP servers list.
|
|
42
|
+
|
|
43
|
+
## Available Tools
|
|
44
|
+
|
|
45
|
+
### Sites
|
|
46
|
+
- `list_sites` - List all sites in your account
|
|
47
|
+
|
|
48
|
+
### Briefs
|
|
49
|
+
- `list_briefs` - List content briefs with optional status filter
|
|
50
|
+
- `create_brief` - Create a new brief (with optional auto-outline generation)
|
|
51
|
+
- `get_brief` - Get brief details including outline and SERP data
|
|
52
|
+
|
|
53
|
+
### Content
|
|
54
|
+
- `list_content` - List content items with optional status filter
|
|
55
|
+
- `get_content` - Get content details including full body text
|
|
56
|
+
|
|
57
|
+
### Research
|
|
58
|
+
- `list_research` - List research sessions
|
|
59
|
+
- `start_research` - Start new AI-powered research on a topic
|
|
60
|
+
|
|
61
|
+
### Jobs
|
|
62
|
+
- `get_job_status` - Check status of async jobs (briefs, research, audits, etc.)
|
|
63
|
+
|
|
64
|
+
### Audits
|
|
65
|
+
- `list_audits` - List site audits
|
|
66
|
+
- `start_audit` - Start a new site audit
|
|
67
|
+
- `get_audit` - Get audit details with pages and issues
|
|
68
|
+
- `export_audit` - Export audit data as CSV
|
|
69
|
+
|
|
70
|
+
### SERP Analysis
|
|
71
|
+
- `analyze_serp` - Analyze search engine results for a query
|
|
72
|
+
- `analyze_competitors` - Analyze competitor content for a query
|
|
73
|
+
- `get_search_intent` - Get search intent classification for a query
|
|
74
|
+
|
|
75
|
+
### AI Visibility
|
|
76
|
+
- `get_ai_visibility` - Get AI visibility metrics for a site
|
|
77
|
+
- `list_prompts` - List monitored AI prompts
|
|
78
|
+
- `create_prompt` - Create a new monitored prompt
|
|
79
|
+
- `get_prompt` - Get prompt details with citations
|
|
80
|
+
- `update_prompt` - Update a monitored prompt
|
|
81
|
+
- `get_competitors` - Get AI visibility competitors
|
|
82
|
+
- `get_alerts` - Get AI visibility alerts
|
|
83
|
+
- `get_insights` - Get AI visibility insights
|
|
84
|
+
|
|
85
|
+
### Analytics
|
|
86
|
+
- `get_gsc_overview` - Get Google Search Console overview metrics
|
|
87
|
+
- `get_gsc_queries` - Get top search queries from GSC
|
|
88
|
+
- `get_gsc_pages` - Get top performing pages from GSC
|
|
89
|
+
- `get_content_gaps` - Get content gap opportunities
|
|
90
|
+
|
|
91
|
+
### Optimizations
|
|
92
|
+
- `list_optimizations` - List optimization sessions
|
|
93
|
+
- `start_optimization` - Start optimization analysis for content
|
|
94
|
+
- `get_optimization` - Get optimization details with suggestions
|
|
95
|
+
- `apply_optimization` - Apply optimization suggestions
|
|
96
|
+
- `get_optimization_insights` - Get optimization insights
|
|
97
|
+
|
|
98
|
+
### Webhooks
|
|
99
|
+
- `list_webhooks` - List configured webhooks
|
|
100
|
+
- `create_webhook` - Create a new webhook
|
|
101
|
+
- `get_webhook` - Get webhook details
|
|
102
|
+
- `update_webhook` - Update a webhook
|
|
103
|
+
- `delete_webhook` - Delete a webhook
|
|
104
|
+
|
|
105
|
+
## Example Conversations
|
|
106
|
+
|
|
107
|
+
### List your sites
|
|
108
|
+
```
|
|
109
|
+
You: What sites do I have connected?
|
|
110
|
+
Claude: [Uses list_sites tool]
|
|
111
|
+
|
|
112
|
+
Here are your connected sites:
|
|
113
|
+
|
|
114
|
+
| Name | Domain | GSC | Created |
|
|
115
|
+
|------|--------|-----|---------|
|
|
116
|
+
| Main Blog | example.com | ✅ | Jan 15, 2024 |
|
|
117
|
+
| Product Site | product.example.com | ❌ | Feb 20, 2024 |
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
### Create a brief with auto-outline
|
|
121
|
+
```
|
|
122
|
+
You: Create a brief about React performance optimization
|
|
123
|
+
Claude: [Uses create_brief with generate_outline=true]
|
|
124
|
+
|
|
125
|
+
## Brief Created
|
|
126
|
+
|
|
127
|
+
- **ID:** abc123
|
|
128
|
+
- **Topic:** React performance optimization
|
|
129
|
+
- **Status:** pending
|
|
130
|
+
|
|
131
|
+
**Outline generation started.** Let me check the status...
|
|
132
|
+
|
|
133
|
+
[Uses get_job_status]
|
|
134
|
+
|
|
135
|
+
The outline is ready! Let me get the details...
|
|
136
|
+
|
|
137
|
+
[Uses get_brief]
|
|
138
|
+
|
|
139
|
+
## Brief: React performance optimization
|
|
140
|
+
|
|
141
|
+
### Outline
|
|
142
|
+
- Introduction to React Performance
|
|
143
|
+
- Common Performance Bottlenecks
|
|
144
|
+
- Optimization Techniques
|
|
145
|
+
- Measuring Performance
|
|
146
|
+
- ...
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
### Research a topic
|
|
150
|
+
```
|
|
151
|
+
You: Research best practices for Next.js SEO
|
|
152
|
+
Claude: [Uses start_research]
|
|
153
|
+
|
|
154
|
+
## Research Started
|
|
155
|
+
|
|
156
|
+
- **ID:** xyz789
|
|
157
|
+
- **Query:** best practices for Next.js SEO
|
|
158
|
+
- **Status:** pending
|
|
159
|
+
|
|
160
|
+
I'll check on the progress...
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
## MCP Resources
|
|
164
|
+
|
|
165
|
+
Browse your Frase data directly in Claude Desktop:
|
|
166
|
+
|
|
167
|
+
- `frase://sites` - List all your sites
|
|
168
|
+
- `frase://sites/{id}` - View individual site details
|
|
169
|
+
- `frase://content` - List all content items
|
|
170
|
+
- `frase://content/{id}` - View content with full body
|
|
171
|
+
- `frase://briefs` - List all briefs
|
|
172
|
+
- `frase://briefs/{id}` - View brief with outline
|
|
173
|
+
|
|
174
|
+
## MCP Prompts
|
|
175
|
+
|
|
176
|
+
Pre-built workflows for common SEO tasks:
|
|
177
|
+
|
|
178
|
+
| Prompt | Description |
|
|
179
|
+
|--------|-------------|
|
|
180
|
+
| `create_seo_article` | Full workflow: research, brief, outline, content generation |
|
|
181
|
+
| `optimize_content` | Analyze content and apply optimization suggestions |
|
|
182
|
+
| `keyword_research` | Research keywords, SERP analysis, and content opportunities |
|
|
183
|
+
| `competitor_analysis` | Analyze competitor content and find gaps |
|
|
184
|
+
| `content_audit` | Run site audit and prioritize improvements |
|
|
185
|
+
|
|
186
|
+
## Configuration
|
|
187
|
+
|
|
188
|
+
| Variable | Required | Description |
|
|
189
|
+
|----------|----------|-------------|
|
|
190
|
+
| `FRASE_API_KEY` | Yes | Your Frase API key |
|
|
191
|
+
| `FRASE_API_URL` | No | Override API URL (default: `https://next.frase.io/api/v1`) |
|
|
192
|
+
| `FRASE_MCP_DEBUG` | No | Enable debug logging (`true`/`false`) |
|
|
193
|
+
|
|
194
|
+
## Development
|
|
195
|
+
|
|
196
|
+
```bash
|
|
197
|
+
# Install dependencies
|
|
198
|
+
npm install
|
|
199
|
+
|
|
200
|
+
# Run in development mode
|
|
201
|
+
npm run dev
|
|
202
|
+
|
|
203
|
+
# Build
|
|
204
|
+
npm run build
|
|
205
|
+
|
|
206
|
+
# Run tests
|
|
207
|
+
npm test
|
|
208
|
+
|
|
209
|
+
# Run integration tests (requires FRASE_TEST_API_KEY)
|
|
210
|
+
npm run test:integration
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
## Troubleshooting
|
|
214
|
+
|
|
215
|
+
### Server not appearing in Claude Desktop
|
|
216
|
+
|
|
217
|
+
1. Verify your config file location:
|
|
218
|
+
- **macOS:** `~/Library/Application Support/Claude/claude_desktop_config.json`
|
|
219
|
+
- **Windows:** `%APPDATA%\Claude\claude_desktop_config.json`
|
|
220
|
+
|
|
221
|
+
2. Check JSON syntax - use a validator if needed
|
|
222
|
+
|
|
223
|
+
3. Restart Claude Desktop completely (quit and reopen)
|
|
224
|
+
|
|
225
|
+
### Authentication errors
|
|
226
|
+
|
|
227
|
+
- Verify your API key is correct
|
|
228
|
+
- Ensure the key has not expired
|
|
229
|
+
- Check that the key has the required permissions
|
|
230
|
+
|
|
231
|
+
### Debug mode
|
|
232
|
+
|
|
233
|
+
Enable debug logging to see detailed information:
|
|
234
|
+
|
|
235
|
+
```json
|
|
236
|
+
{
|
|
237
|
+
"mcpServers": {
|
|
238
|
+
"frase": {
|
|
239
|
+
"command": "npx",
|
|
240
|
+
"args": ["-y", "@frase/mcp-server"],
|
|
241
|
+
"env": {
|
|
242
|
+
"FRASE_API_KEY": "your_key",
|
|
243
|
+
"FRASE_MCP_DEBUG": "true"
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
### Rate limiting
|
|
251
|
+
|
|
252
|
+
The server automatically retries on rate limits with exponential backoff. If you're hitting limits frequently, consider spacing out your requests.
|
|
253
|
+
|
|
254
|
+
## Support
|
|
255
|
+
|
|
256
|
+
- [Frase Documentation](https://docs.frase.io)
|
|
257
|
+
- [API Documentation](https://docs.frase.io/api)
|
|
258
|
+
- [GitHub Issues](https://github.com/frase-io/frase/issues)
|
|
259
|
+
|
|
260
|
+
## License
|
|
261
|
+
|
|
262
|
+
MIT
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Frase API Client with retry logic and caching
|
|
3
|
+
*/
|
|
4
|
+
import type { Config } from "./config.js";
|
|
5
|
+
/**
|
|
6
|
+
* API Error class
|
|
7
|
+
*/
|
|
8
|
+
export declare class FraseApiError extends Error {
|
|
9
|
+
readonly status: number;
|
|
10
|
+
readonly code: string;
|
|
11
|
+
readonly details?: unknown | undefined;
|
|
12
|
+
constructor(status: number, code: string, message: string, details?: unknown | undefined);
|
|
13
|
+
/**
|
|
14
|
+
* Check if this error is retryable
|
|
15
|
+
*/
|
|
16
|
+
get isRetryable(): boolean;
|
|
17
|
+
/**
|
|
18
|
+
* Format error for display
|
|
19
|
+
*/
|
|
20
|
+
toMarkdown(): string;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Paginated response structure
|
|
24
|
+
*/
|
|
25
|
+
export interface PaginatedResponse<T> {
|
|
26
|
+
data: T[];
|
|
27
|
+
pagination: {
|
|
28
|
+
page: number;
|
|
29
|
+
page_size: number;
|
|
30
|
+
total: number;
|
|
31
|
+
has_more: boolean;
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* API response structure
|
|
36
|
+
*/
|
|
37
|
+
export interface ApiResponse<T> {
|
|
38
|
+
data: T;
|
|
39
|
+
request_id: string;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Frase API Client
|
|
43
|
+
*/
|
|
44
|
+
export declare class FraseApiClient {
|
|
45
|
+
private config;
|
|
46
|
+
private cache;
|
|
47
|
+
constructor(config: Config);
|
|
48
|
+
/**
|
|
49
|
+
* Sleep for a specified duration
|
|
50
|
+
*/
|
|
51
|
+
private sleep;
|
|
52
|
+
/**
|
|
53
|
+
* Calculate retry delay with exponential backoff
|
|
54
|
+
*/
|
|
55
|
+
private getRetryDelay;
|
|
56
|
+
/**
|
|
57
|
+
* Make an HTTP request with retry logic
|
|
58
|
+
*/
|
|
59
|
+
request<T>(method: string, path: string, options?: {
|
|
60
|
+
body?: unknown;
|
|
61
|
+
params?: Record<string, string | number | boolean | undefined>;
|
|
62
|
+
cacheTtl?: number;
|
|
63
|
+
}): Promise<T>;
|
|
64
|
+
/**
|
|
65
|
+
* GET request
|
|
66
|
+
*/
|
|
67
|
+
get<T>(path: string, params?: Record<string, string | number | boolean | undefined>, cacheTtl?: number): Promise<T>;
|
|
68
|
+
/**
|
|
69
|
+
* POST request
|
|
70
|
+
*/
|
|
71
|
+
post<T>(path: string, body: unknown): Promise<T>;
|
|
72
|
+
/**
|
|
73
|
+
* PUT request
|
|
74
|
+
*/
|
|
75
|
+
put<T>(path: string, body: unknown): Promise<T>;
|
|
76
|
+
/**
|
|
77
|
+
* PATCH request
|
|
78
|
+
*/
|
|
79
|
+
patch<T>(path: string, body: unknown): Promise<T>;
|
|
80
|
+
/**
|
|
81
|
+
* DELETE request
|
|
82
|
+
*/
|
|
83
|
+
delete<T>(path: string): Promise<T>;
|
|
84
|
+
/**
|
|
85
|
+
* Clear all cached data
|
|
86
|
+
*/
|
|
87
|
+
clearCache(): void;
|
|
88
|
+
/**
|
|
89
|
+
* Destroy the client (for graceful shutdown)
|
|
90
|
+
*/
|
|
91
|
+
destroy(): void;
|
|
92
|
+
}
|
|
93
|
+
//# sourceMappingURL=api-client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"api-client.d.ts","sourceRoot":"","sources":["../src/api-client.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAa1C;;GAEG;AACH,qBAAa,aAAc,SAAQ,KAAK;aAEpB,MAAM,EAAE,MAAM;aACd,IAAI,EAAE,MAAM;aAEZ,OAAO,CAAC,EAAE,OAAO;gBAHjB,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,MAAM,EAC5B,OAAO,EAAE,MAAM,EACC,OAAO,CAAC,EAAE,OAAO,YAAA;IAMnC;;OAEG;IACH,IAAI,WAAW,IAAI,OAAO,CAEzB;IAED;;OAEG;IACH,UAAU,IAAI,MAAM;CAWrB;AAED;;GAEG;AACH,MAAM,WAAW,iBAAiB,CAAC,CAAC;IAClC,IAAI,EAAE,CAAC,EAAE,CAAC;IACV,UAAU,EAAE;QACV,IAAI,EAAE,MAAM,CAAC;QACb,SAAS,EAAE,MAAM,CAAC;QAClB,KAAK,EAAE,MAAM,CAAC;QACd,QAAQ,EAAE,OAAO,CAAC;KACnB,CAAC;CACH;AAED;;GAEG;AACH,MAAM,WAAW,WAAW,CAAC,CAAC;IAC5B,IAAI,EAAE,CAAC,CAAC;IACR,UAAU,EAAE,MAAM,CAAC;CACpB;AAED;;GAEG;AACH,qBAAa,cAAc;IAGb,OAAO,CAAC,MAAM;IAF1B,OAAO,CAAC,KAAK,CAAgB;gBAET,MAAM,EAAE,MAAM;IAIlC;;OAEG;IACH,OAAO,CAAC,KAAK;IAIb;;OAEG;IACH,OAAO,CAAC,aAAa;IAarB;;OAEG;IACG,OAAO,CAAC,CAAC,EACb,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,MAAM,EACZ,OAAO,CAAC,EAAE;QACR,IAAI,CAAC,EAAE,OAAO,CAAC;QACf,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,SAAS,CAAC,CAAC;QAC/D,QAAQ,CAAC,EAAE,MAAM,CAAC;KACnB,GACA,OAAO,CAAC,CAAC,CAAC;IAiHb;;OAEG;IACH,GAAG,CAAC,CAAC,EACH,IAAI,EAAE,MAAM,EACZ,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,SAAS,CAAC,EAC9D,QAAQ,CAAC,EAAE,MAAM,GAChB,OAAO,CAAC,CAAC,CAAC;IAIb;;OAEG;IACH,IAAI,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,GAAG,OAAO,CAAC,CAAC,CAAC;IAIhD;;OAEG;IACH,GAAG,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,GAAG,OAAO,CAAC,CAAC,CAAC;IAI/C;;OAEG;IACH,KAAK,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,GAAG,OAAO,CAAC,CAAC,CAAC;IAIjD;;OAEG;IACH,MAAM,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,CAAC;IAInC;;OAEG;IACH,UAAU,IAAI,IAAI;IAIlB;;OAEG;IACH,OAAO,IAAI,IAAI;CAGhB"}
|
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Frase API Client with retry logic and caching
|
|
3
|
+
*/
|
|
4
|
+
import { ResponseCache } from "./cache.js";
|
|
5
|
+
import { debugLog } from "./config.js";
|
|
6
|
+
/**
|
|
7
|
+
* Retry configuration
|
|
8
|
+
*/
|
|
9
|
+
const RETRY_CONFIG = {
|
|
10
|
+
maxRetries: 2,
|
|
11
|
+
baseDelayMs: 1000,
|
|
12
|
+
maxDelayMs: 10000,
|
|
13
|
+
retryableStatuses: [429, 500, 502, 503, 504],
|
|
14
|
+
};
|
|
15
|
+
/**
|
|
16
|
+
* API Error class
|
|
17
|
+
*/
|
|
18
|
+
export class FraseApiError extends Error {
|
|
19
|
+
status;
|
|
20
|
+
code;
|
|
21
|
+
details;
|
|
22
|
+
constructor(status, code, message, details) {
|
|
23
|
+
super(message);
|
|
24
|
+
this.status = status;
|
|
25
|
+
this.code = code;
|
|
26
|
+
this.details = details;
|
|
27
|
+
this.name = "FraseApiError";
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Check if this error is retryable
|
|
31
|
+
*/
|
|
32
|
+
get isRetryable() {
|
|
33
|
+
return RETRY_CONFIG.retryableStatuses.includes(this.status);
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Format error for display
|
|
37
|
+
*/
|
|
38
|
+
toMarkdown() {
|
|
39
|
+
let md = `**Error:** ${this.message}\n`;
|
|
40
|
+
md += `- Code: \`${this.code}\`\n`;
|
|
41
|
+
md += `- Status: ${this.status}\n`;
|
|
42
|
+
if (this.details) {
|
|
43
|
+
md += `- Details: ${JSON.stringify(this.details)}\n`;
|
|
44
|
+
}
|
|
45
|
+
return md;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Frase API Client
|
|
50
|
+
*/
|
|
51
|
+
export class FraseApiClient {
|
|
52
|
+
config;
|
|
53
|
+
cache;
|
|
54
|
+
constructor(config) {
|
|
55
|
+
this.config = config;
|
|
56
|
+
this.cache = new ResponseCache();
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Sleep for a specified duration
|
|
60
|
+
*/
|
|
61
|
+
sleep(ms) {
|
|
62
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Calculate retry delay with exponential backoff
|
|
66
|
+
*/
|
|
67
|
+
getRetryDelay(attempt, retryAfter) {
|
|
68
|
+
if (retryAfter) {
|
|
69
|
+
const seconds = parseInt(retryAfter, 10);
|
|
70
|
+
if (!isNaN(seconds)) {
|
|
71
|
+
return Math.min(seconds * 1000, RETRY_CONFIG.maxDelayMs);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
// Exponential backoff: 1s, 2s, 4s... capped at maxDelayMs
|
|
75
|
+
const delay = RETRY_CONFIG.baseDelayMs * Math.pow(2, attempt);
|
|
76
|
+
return Math.min(delay, RETRY_CONFIG.maxDelayMs);
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Make an HTTP request with retry logic
|
|
80
|
+
*/
|
|
81
|
+
async request(method, path, options) {
|
|
82
|
+
const { body, params, cacheTtl } = options || {};
|
|
83
|
+
// Build URL with query params
|
|
84
|
+
let url = `${this.config.apiUrl}${path}`;
|
|
85
|
+
if (params) {
|
|
86
|
+
const searchParams = new URLSearchParams();
|
|
87
|
+
for (const [key, value] of Object.entries(params)) {
|
|
88
|
+
if (value !== undefined) {
|
|
89
|
+
searchParams.set(key, String(value));
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
const queryString = searchParams.toString();
|
|
93
|
+
if (queryString) {
|
|
94
|
+
url += `?${queryString}`;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
// Check cache for GET requests
|
|
98
|
+
if (method === "GET" && cacheTtl) {
|
|
99
|
+
const cacheKey = ResponseCache.key(method, path, params);
|
|
100
|
+
const cached = this.cache.get(cacheKey);
|
|
101
|
+
if (cached) {
|
|
102
|
+
debugLog(this.config, `Cache hit: ${path}`);
|
|
103
|
+
return cached;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
let lastError = null;
|
|
107
|
+
for (let attempt = 0; attempt <= RETRY_CONFIG.maxRetries; attempt++) {
|
|
108
|
+
try {
|
|
109
|
+
debugLog(this.config, `Request: ${method} ${path} (attempt ${attempt + 1})`);
|
|
110
|
+
const response = await fetch(url, {
|
|
111
|
+
method,
|
|
112
|
+
headers: {
|
|
113
|
+
"Content-Type": "application/json",
|
|
114
|
+
"X-API-KEY": this.config.apiKey,
|
|
115
|
+
"User-Agent": "frase-mcp-server/0.1.0",
|
|
116
|
+
},
|
|
117
|
+
body: body ? JSON.stringify(body) : undefined,
|
|
118
|
+
});
|
|
119
|
+
if (!response.ok) {
|
|
120
|
+
let errorData = {};
|
|
121
|
+
try {
|
|
122
|
+
errorData = await response.json();
|
|
123
|
+
}
|
|
124
|
+
catch {
|
|
125
|
+
// Ignore JSON parse errors
|
|
126
|
+
}
|
|
127
|
+
const error = new FraseApiError(response.status, errorData.error?.code || `http_${response.status}`, errorData.error?.message || `HTTP ${response.status}`, errorData);
|
|
128
|
+
// Check if we should retry
|
|
129
|
+
if (attempt < RETRY_CONFIG.maxRetries && error.isRetryable) {
|
|
130
|
+
const delay = this.getRetryDelay(attempt, response.headers.get("Retry-After") || undefined);
|
|
131
|
+
debugLog(this.config, `Retrying in ${delay}ms...`);
|
|
132
|
+
await this.sleep(delay);
|
|
133
|
+
continue;
|
|
134
|
+
}
|
|
135
|
+
throw error;
|
|
136
|
+
}
|
|
137
|
+
const data = (await response.json());
|
|
138
|
+
// Cache successful GET responses
|
|
139
|
+
if (method === "GET" && cacheTtl) {
|
|
140
|
+
const cacheKey = ResponseCache.key(method, path, params);
|
|
141
|
+
this.cache.set(cacheKey, data, cacheTtl);
|
|
142
|
+
}
|
|
143
|
+
// Invalidate cache on write operations
|
|
144
|
+
if (method !== "GET") {
|
|
145
|
+
const resourceType = path.split("/")[1]; // e.g., /sites -> sites
|
|
146
|
+
if (resourceType) {
|
|
147
|
+
this.cache.invalidateResourceType(resourceType);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
return data;
|
|
151
|
+
}
|
|
152
|
+
catch (error) {
|
|
153
|
+
lastError = error;
|
|
154
|
+
// Don't retry FraseApiError unless it's retryable
|
|
155
|
+
if (error instanceof FraseApiError && !error.isRetryable) {
|
|
156
|
+
throw error;
|
|
157
|
+
}
|
|
158
|
+
// Network errors - retry if we have attempts left
|
|
159
|
+
if (attempt < RETRY_CONFIG.maxRetries && !(error instanceof FraseApiError)) {
|
|
160
|
+
const delay = this.getRetryDelay(attempt);
|
|
161
|
+
debugLog(this.config, `Network error, retrying in ${delay}ms...`);
|
|
162
|
+
await this.sleep(delay);
|
|
163
|
+
continue;
|
|
164
|
+
}
|
|
165
|
+
throw error;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
throw lastError || new Error("Request failed after retries");
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* GET request
|
|
172
|
+
*/
|
|
173
|
+
get(path, params, cacheTtl) {
|
|
174
|
+
return this.request("GET", path, { params, cacheTtl });
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* POST request
|
|
178
|
+
*/
|
|
179
|
+
post(path, body) {
|
|
180
|
+
return this.request("POST", path, { body });
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* PUT request
|
|
184
|
+
*/
|
|
185
|
+
put(path, body) {
|
|
186
|
+
return this.request("PUT", path, { body });
|
|
187
|
+
}
|
|
188
|
+
/**
|
|
189
|
+
* PATCH request
|
|
190
|
+
*/
|
|
191
|
+
patch(path, body) {
|
|
192
|
+
return this.request("PATCH", path, { body });
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* DELETE request
|
|
196
|
+
*/
|
|
197
|
+
delete(path) {
|
|
198
|
+
return this.request("DELETE", path);
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* Clear all cached data
|
|
202
|
+
*/
|
|
203
|
+
clearCache() {
|
|
204
|
+
this.cache.clear();
|
|
205
|
+
}
|
|
206
|
+
/**
|
|
207
|
+
* Destroy the client (for graceful shutdown)
|
|
208
|
+
*/
|
|
209
|
+
destroy() {
|
|
210
|
+
this.cache.destroy();
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
//# sourceMappingURL=api-client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"api-client.js","sourceRoot":"","sources":["../src/api-client.ts"],"names":[],"mappings":"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"}
|
package/dist/cache.d.ts
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Simple in-memory cache for GET responses
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* Cache TTL configuration (in milliseconds)
|
|
6
|
+
*/
|
|
7
|
+
export declare const CACHE_TTL: {
|
|
8
|
+
readonly lists: 60000;
|
|
9
|
+
readonly resources: 300000;
|
|
10
|
+
};
|
|
11
|
+
/**
|
|
12
|
+
* Simple cache implementation
|
|
13
|
+
*/
|
|
14
|
+
export declare class ResponseCache {
|
|
15
|
+
private cache;
|
|
16
|
+
private cleanupInterval;
|
|
17
|
+
constructor();
|
|
18
|
+
/**
|
|
19
|
+
* Get a cached value
|
|
20
|
+
*/
|
|
21
|
+
get<T>(key: string): T | undefined;
|
|
22
|
+
/**
|
|
23
|
+
* Set a cached value
|
|
24
|
+
*/
|
|
25
|
+
set<T>(key: string, data: T, ttl: number): void;
|
|
26
|
+
/**
|
|
27
|
+
* Generate cache key for a request
|
|
28
|
+
*/
|
|
29
|
+
static key(method: string, path: string, params?: Record<string, unknown>): string;
|
|
30
|
+
/**
|
|
31
|
+
* Invalidate cache entries matching a pattern
|
|
32
|
+
*/
|
|
33
|
+
invalidate(pattern: string | RegExp): void;
|
|
34
|
+
/**
|
|
35
|
+
* Invalidate all cache entries for a resource type
|
|
36
|
+
* Called after write operations
|
|
37
|
+
*/
|
|
38
|
+
invalidateResourceType(resourceType: string): void;
|
|
39
|
+
/**
|
|
40
|
+
* Clear all cached entries
|
|
41
|
+
*/
|
|
42
|
+
clear(): void;
|
|
43
|
+
/**
|
|
44
|
+
* Clean up expired entries
|
|
45
|
+
*/
|
|
46
|
+
private cleanup;
|
|
47
|
+
/**
|
|
48
|
+
* Stop the cleanup interval (for graceful shutdown)
|
|
49
|
+
*/
|
|
50
|
+
destroy(): void;
|
|
51
|
+
}
|
|
52
|
+
//# sourceMappingURL=cache.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cache.d.ts","sourceRoot":"","sources":["../src/cache.ts"],"names":[],"mappings":"AAAA;;GAEG;AAOH;;GAEG;AACH,eAAO,MAAM,SAAS;;;CAGZ,CAAC;AAEX;;GAEG;AACH,qBAAa,aAAa;IACxB,OAAO,CAAC,KAAK,CAA0C;IACvD,OAAO,CAAC,eAAe,CAA+B;;IAOtD;;OAEG;IACH,GAAG,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,GAAG,CAAC,GAAG,SAAS;IAelC;;OAEG;IACH,GAAG,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI;IAO/C;;OAEG;IACH,MAAM,CAAC,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM;IAKlF;;OAEG;IACH,UAAU,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI;IAU1C;;;OAGG;IACH,sBAAsB,CAAC,YAAY,EAAE,MAAM,GAAG,IAAI;IAIlD;;OAEG;IACH,KAAK,IAAI,IAAI;IAIb;;OAEG;IACH,OAAO,CAAC,OAAO;IAUf;;OAEG;IACH,OAAO,IAAI,IAAI;CAOhB"}
|