@specforge/mcp 1.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/README.md +345 -0
- package/bin/specforge-mcp +3 -0
- package/dist/client/api-client.d.ts +110 -0
- package/dist/client/api-client.d.ts.map +1 -0
- package/dist/client/api-client.js +170 -0
- package/dist/client/api-client.js.map +1 -0
- package/dist/config/index.d.ts +71 -0
- package/dist/config/index.d.ts.map +1 -0
- package/dist/config/index.js +120 -0
- package/dist/config/index.js.map +1 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +60 -0
- package/dist/index.js.map +1 -0
- package/dist/server.d.ts +22 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +100 -0
- package/dist/server.js.map +1 -0
- package/dist/tools/index.d.ts +79 -0
- package/dist/tools/index.d.ts.map +1 -0
- package/dist/tools/index.js +1622 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/types/index.d.ts +329 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +7 -0
- package/dist/types/index.js.map +1 -0
- package/dist/validation/index.d.ts +86 -0
- package/dist/validation/index.d.ts.map +1 -0
- package/dist/validation/index.js +442 -0
- package/dist/validation/index.js.map +1 -0
- package/package.json +46 -0
package/README.md
ADDED
|
@@ -0,0 +1,345 @@
|
|
|
1
|
+
# @specforge/mcp
|
|
2
|
+
|
|
3
|
+
MCP (Model Context Protocol) server for SpecForge - enables AI agents to interact with your projects, specifications, epics, and tickets.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install -g @specforge/mcp
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Or install locally:
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
npm install @specforge/mcp
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Getting Your API Key
|
|
18
|
+
|
|
19
|
+
1. Log in to the SpecForge webapp
|
|
20
|
+
2. Navigate to **Settings > API Keys**
|
|
21
|
+
3. Click **Create New API Key**
|
|
22
|
+
4. Select the permissions you need (read, write, or both)
|
|
23
|
+
5. Copy the key immediately - it's only shown once!
|
|
24
|
+
|
|
25
|
+
API keys have the format: `sf_live_xxxxxxxxxxxxxxxxxxxxxx`
|
|
26
|
+
|
|
27
|
+
## Environment Variables
|
|
28
|
+
|
|
29
|
+
| Variable | Required | Description |
|
|
30
|
+
|----------|----------|-------------|
|
|
31
|
+
| `SPECFORGE_API_KEY` | Yes | Your API key from the webapp |
|
|
32
|
+
| `SPECFORGE_API_URL` | Yes | The MCP API Gateway URL from your deployment |
|
|
33
|
+
| `SPECFORGE_DEBUG` | No | Set to `true` to enable debug logging |
|
|
34
|
+
|
|
35
|
+
## AI Client Configuration
|
|
36
|
+
|
|
37
|
+
### Claude Desktop
|
|
38
|
+
|
|
39
|
+
Add to `~/.config/claude/claude_desktop_config.json` (Linux/macOS) or `%APPDATA%\Claude\claude_desktop_config.json` (Windows):
|
|
40
|
+
|
|
41
|
+
```json
|
|
42
|
+
{
|
|
43
|
+
"mcpServers": {
|
|
44
|
+
"specforge": {
|
|
45
|
+
"command": "specforge-mcp",
|
|
46
|
+
"env": {
|
|
47
|
+
"SPECFORGE_API_KEY": "sf_live_your_api_key_here",
|
|
48
|
+
"SPECFORGE_API_URL": "https://your-api-gateway-url.amazonaws.com/prod"
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### Claude Code (CLI)
|
|
56
|
+
|
|
57
|
+
Add to `~/.claude/settings.json`:
|
|
58
|
+
|
|
59
|
+
```json
|
|
60
|
+
{
|
|
61
|
+
"mcpServers": {
|
|
62
|
+
"specforge": {
|
|
63
|
+
"command": "specforge-mcp",
|
|
64
|
+
"env": {
|
|
65
|
+
"SPECFORGE_API_KEY": "sf_live_your_api_key_here",
|
|
66
|
+
"SPECFORGE_API_URL": "https://your-api-gateway-url.amazonaws.com/prod"
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
Or set environment variables in your shell and run:
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
export SPECFORGE_API_KEY="sf_live_your_api_key_here"
|
|
77
|
+
export SPECFORGE_API_URL="https://your-api-gateway-url.amazonaws.com/prod"
|
|
78
|
+
specforge-mcp
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
### Cursor
|
|
82
|
+
|
|
83
|
+
Add to your Cursor MCP configuration:
|
|
84
|
+
|
|
85
|
+
```json
|
|
86
|
+
{
|
|
87
|
+
"mcpServers": {
|
|
88
|
+
"specforge": {
|
|
89
|
+
"command": "npx",
|
|
90
|
+
"args": ["-y", "@specforge/mcp"],
|
|
91
|
+
"env": {
|
|
92
|
+
"SPECFORGE_API_KEY": "sf_live_your_api_key_here",
|
|
93
|
+
"SPECFORGE_API_URL": "https://your-api-gateway-url.amazonaws.com/prod"
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
### GitHub Copilot VS Code Extension
|
|
101
|
+
|
|
102
|
+
Add to your VS Code `settings.json`:
|
|
103
|
+
|
|
104
|
+
```json
|
|
105
|
+
{
|
|
106
|
+
"github.copilot.advanced": {
|
|
107
|
+
"mcp": {
|
|
108
|
+
"servers": {
|
|
109
|
+
"specforge": {
|
|
110
|
+
"command": "specforge-mcp",
|
|
111
|
+
"env": {
|
|
112
|
+
"SPECFORGE_API_KEY": "sf_live_your_api_key_here",
|
|
113
|
+
"SPECFORGE_API_URL": "https://your-api-gateway-url.amazonaws.com/prod"
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
### GitHub Copilot CLI
|
|
123
|
+
|
|
124
|
+
Create a config file at `~/.config/gh-copilot/mcp.json`:
|
|
125
|
+
|
|
126
|
+
```json
|
|
127
|
+
{
|
|
128
|
+
"servers": {
|
|
129
|
+
"specforge": {
|
|
130
|
+
"command": "specforge-mcp",
|
|
131
|
+
"env": {
|
|
132
|
+
"SPECFORGE_API_KEY": "sf_live_your_api_key_here",
|
|
133
|
+
"SPECFORGE_API_URL": "https://your-api-gateway-url.amazonaws.com/prod"
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
### OpenAI Codex CLI
|
|
141
|
+
|
|
142
|
+
Configure using environment variables:
|
|
143
|
+
|
|
144
|
+
```bash
|
|
145
|
+
export SPECFORGE_API_KEY="sf_live_your_api_key_here"
|
|
146
|
+
export SPECFORGE_API_URL="https://your-api-gateway-url.amazonaws.com/prod"
|
|
147
|
+
|
|
148
|
+
# Then configure your Codex CLI to use the MCP server
|
|
149
|
+
codex --mcp-server specforge-mcp
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
### ChatGPT Desktop
|
|
153
|
+
|
|
154
|
+
Add to ChatGPT Desktop's MCP configuration:
|
|
155
|
+
|
|
156
|
+
```json
|
|
157
|
+
{
|
|
158
|
+
"mcpServers": {
|
|
159
|
+
"specforge": {
|
|
160
|
+
"command": "specforge-mcp",
|
|
161
|
+
"env": {
|
|
162
|
+
"SPECFORGE_API_KEY": "sf_live_your_api_key_here",
|
|
163
|
+
"SPECFORGE_API_URL": "https://your-api-gateway-url.amazonaws.com/prod"
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
### Windsurf
|
|
171
|
+
|
|
172
|
+
Add to Windsurf's settings:
|
|
173
|
+
|
|
174
|
+
```json
|
|
175
|
+
{
|
|
176
|
+
"mcp": {
|
|
177
|
+
"servers": {
|
|
178
|
+
"specforge": {
|
|
179
|
+
"command": "specforge-mcp",
|
|
180
|
+
"env": {
|
|
181
|
+
"SPECFORGE_API_KEY": "sf_live_your_api_key_here",
|
|
182
|
+
"SPECFORGE_API_URL": "https://your-api-gateway-url.amazonaws.com/prod"
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
### Generic MCP Client
|
|
191
|
+
|
|
192
|
+
For any MCP-compatible client, the server uses stdio transport:
|
|
193
|
+
|
|
194
|
+
```bash
|
|
195
|
+
# Start the server with environment variables
|
|
196
|
+
SPECFORGE_API_KEY="sf_live_xxx" SPECFORGE_API_URL="https://xxx" specforge-mcp
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
The server communicates via JSON-RPC over stdin/stdout.
|
|
200
|
+
|
|
201
|
+
## Available Tools
|
|
202
|
+
|
|
203
|
+
The SpecForge MCP server provides the following tool categories:
|
|
204
|
+
|
|
205
|
+
### Core Operations
|
|
206
|
+
- `list_projects` - List all accessible projects
|
|
207
|
+
- `get_project` - Get project details
|
|
208
|
+
- `list_specifications` - List specifications in a project
|
|
209
|
+
- `get_specification` - Get specification details
|
|
210
|
+
- `list_epics` - List epics in a specification
|
|
211
|
+
- `get_epic` - Get epic details
|
|
212
|
+
- `list_tickets` - List tickets in an epic
|
|
213
|
+
- `get_ticket` - Get ticket details with implementation steps
|
|
214
|
+
|
|
215
|
+
### Context & AI Tools
|
|
216
|
+
- `get_implementation_context` - Get full context for implementing a ticket
|
|
217
|
+
- `get_next_actionable_tickets` - Get tickets ready to work on
|
|
218
|
+
- `get_blocked_tickets` - Get blocked tickets with reasons
|
|
219
|
+
- `get_critical_path` - Get the critical implementation path
|
|
220
|
+
|
|
221
|
+
### Workflow & Tracking
|
|
222
|
+
- `start_work_session` - Start working on a ticket
|
|
223
|
+
- `complete_work_session` - Mark work complete with summary
|
|
224
|
+
- `report_progress` - Report progress on a ticket
|
|
225
|
+
|
|
226
|
+
### Testing Tools
|
|
227
|
+
- `report_test_results` - Report test results for a ticket
|
|
228
|
+
- `get_ticket_test_status` - Get test status
|
|
229
|
+
- `validate_ticket_completion` - Validate completion criteria
|
|
230
|
+
|
|
231
|
+
### Discovery Tools
|
|
232
|
+
- `report_discovery` - Report bugs, tech debt, or new requirements
|
|
233
|
+
- `get_pending_discoveries` - Get unresolved discoveries
|
|
234
|
+
- `resolve_discovery` - Mark a discovery as resolved
|
|
235
|
+
|
|
236
|
+
### Status & Analytics
|
|
237
|
+
- `get_specification_status` - Get specification progress
|
|
238
|
+
- `get_epic_status` - Get epic progress
|
|
239
|
+
- `get_implementation_summary` - Get overall project summary
|
|
240
|
+
- `get_blockers_report` - Get blocking dependencies
|
|
241
|
+
|
|
242
|
+
### Search Tools
|
|
243
|
+
- `search_tickets` - Search tickets by text
|
|
244
|
+
- `find_tickets_by_file` - Find tickets affecting a file
|
|
245
|
+
- `find_tickets_by_tag` - Find tickets with specific tags
|
|
246
|
+
- `find_related_tickets` - Find related tickets
|
|
247
|
+
|
|
248
|
+
### Git Integration
|
|
249
|
+
- `link_commit` - Link a commit to a ticket
|
|
250
|
+
- `link_pull_request` - Link a PR to a ticket
|
|
251
|
+
- `get_ticket_commits` - Get commits for a ticket
|
|
252
|
+
- `get_ticket_prs` - Get PRs for a ticket
|
|
253
|
+
|
|
254
|
+
## Troubleshooting
|
|
255
|
+
|
|
256
|
+
### "SPECFORGE_API_KEY environment variable is required"
|
|
257
|
+
|
|
258
|
+
Make sure you've set the `SPECFORGE_API_KEY` environment variable with your API key from the SpecForge webapp.
|
|
259
|
+
|
|
260
|
+
```bash
|
|
261
|
+
# Check if it's set
|
|
262
|
+
echo $SPECFORGE_API_KEY
|
|
263
|
+
|
|
264
|
+
# Set it
|
|
265
|
+
export SPECFORGE_API_KEY="sf_live_your_key_here"
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
### "Invalid API key format"
|
|
269
|
+
|
|
270
|
+
API keys must start with `sf_live_`. If your key doesn't match this format, regenerate it in the webapp.
|
|
271
|
+
|
|
272
|
+
### "SPECFORGE_API_URL environment variable is required"
|
|
273
|
+
|
|
274
|
+
You need to provide the API Gateway URL from your SpecForge deployment. This is output when you deploy the backend.
|
|
275
|
+
|
|
276
|
+
### Connection Timeouts
|
|
277
|
+
|
|
278
|
+
If you're experiencing connection issues:
|
|
279
|
+
|
|
280
|
+
1. Verify your API URL is correct
|
|
281
|
+
2. Check your network connection
|
|
282
|
+
3. Enable debug mode to see detailed logs:
|
|
283
|
+
|
|
284
|
+
```bash
|
|
285
|
+
export SPECFORGE_DEBUG=true
|
|
286
|
+
specforge-mcp
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
### Debug Mode
|
|
290
|
+
|
|
291
|
+
Enable debug logging to troubleshoot issues:
|
|
292
|
+
|
|
293
|
+
```bash
|
|
294
|
+
export SPECFORGE_DEBUG=true
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
This will output detailed logs to stderr, including:
|
|
298
|
+
- Configuration loaded
|
|
299
|
+
- Tool calls received
|
|
300
|
+
- API requests made
|
|
301
|
+
- Response data
|
|
302
|
+
|
|
303
|
+
### Server Not Starting
|
|
304
|
+
|
|
305
|
+
1. Ensure Node.js 18+ is installed
|
|
306
|
+
2. Check that the package is installed globally: `npm list -g @specforge/mcp`
|
|
307
|
+
3. Try reinstalling: `npm install -g @specforge/mcp`
|
|
308
|
+
|
|
309
|
+
### Permission Denied
|
|
310
|
+
|
|
311
|
+
If you get "permission denied" when running `specforge-mcp`:
|
|
312
|
+
|
|
313
|
+
```bash
|
|
314
|
+
# On macOS/Linux
|
|
315
|
+
chmod +x $(which specforge-mcp)
|
|
316
|
+
|
|
317
|
+
# Or run with node directly
|
|
318
|
+
node $(npm root -g)/@specforge/mcp/dist/index.js
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
## Development
|
|
322
|
+
|
|
323
|
+
To contribute to the MCP server:
|
|
324
|
+
|
|
325
|
+
```bash
|
|
326
|
+
# Clone the repo
|
|
327
|
+
git clone https://github.com/your-org/specforge.git
|
|
328
|
+
cd specforge/mcp
|
|
329
|
+
|
|
330
|
+
# Install dependencies
|
|
331
|
+
npm install
|
|
332
|
+
|
|
333
|
+
# Build
|
|
334
|
+
npm run build
|
|
335
|
+
|
|
336
|
+
# Link for local testing
|
|
337
|
+
npm link
|
|
338
|
+
|
|
339
|
+
# Run in development mode
|
|
340
|
+
npm run dev
|
|
341
|
+
```
|
|
342
|
+
|
|
343
|
+
## License
|
|
344
|
+
|
|
345
|
+
MIT
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SpecForge API Client
|
|
3
|
+
*
|
|
4
|
+
* HTTP client for communicating with the SpecForge API Gateway.
|
|
5
|
+
* Handles authentication, request/response formatting, and error handling.
|
|
6
|
+
*/
|
|
7
|
+
import { McpConfig } from '../config/index.js';
|
|
8
|
+
/**
|
|
9
|
+
* API response structure returned by SpecForge API
|
|
10
|
+
*/
|
|
11
|
+
export interface ApiResponse<T = unknown> {
|
|
12
|
+
/** Response data on success */
|
|
13
|
+
data?: T;
|
|
14
|
+
/** Error message on failure */
|
|
15
|
+
error?: string;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Result of API key validation
|
|
19
|
+
*/
|
|
20
|
+
export interface ApiKeyValidationResult {
|
|
21
|
+
/** Whether the API key is valid */
|
|
22
|
+
valid: boolean;
|
|
23
|
+
/** User ID associated with the key (if valid) */
|
|
24
|
+
userId?: string;
|
|
25
|
+
/** Permissions granted to the key (if valid) */
|
|
26
|
+
permissions?: {
|
|
27
|
+
read?: boolean;
|
|
28
|
+
write?: boolean;
|
|
29
|
+
admin?: boolean;
|
|
30
|
+
};
|
|
31
|
+
/** Error message (if invalid) */
|
|
32
|
+
message?: string;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Options for API requests
|
|
36
|
+
*/
|
|
37
|
+
export interface ApiRequestOptions {
|
|
38
|
+
/** Request timeout in milliseconds */
|
|
39
|
+
timeout?: number;
|
|
40
|
+
/** Additional headers to include */
|
|
41
|
+
headers?: Record<string, string>;
|
|
42
|
+
/** Signal for aborting the request */
|
|
43
|
+
signal?: AbortSignal;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* API Client for SpecForge
|
|
47
|
+
*
|
|
48
|
+
* @example
|
|
49
|
+
* ```typescript
|
|
50
|
+
* import { getConfig } from './config/index.js';
|
|
51
|
+
* import { ApiClient } from './client/api-client.js';
|
|
52
|
+
*
|
|
53
|
+
* const config = getConfig();
|
|
54
|
+
* const client = new ApiClient(config);
|
|
55
|
+
*
|
|
56
|
+
* // Make an API call
|
|
57
|
+
* const projects = await client.call('list_projects', { userId: 'user123' });
|
|
58
|
+
* ```
|
|
59
|
+
*/
|
|
60
|
+
export declare class ApiClient {
|
|
61
|
+
private config;
|
|
62
|
+
constructor(config: McpConfig);
|
|
63
|
+
/**
|
|
64
|
+
* Make an API call to SpecForge
|
|
65
|
+
*
|
|
66
|
+
* @param operation - Name of the operation to execute
|
|
67
|
+
* @param args - Arguments for the operation
|
|
68
|
+
* @param options - Optional request configuration
|
|
69
|
+
* @returns Promise resolving to the operation result
|
|
70
|
+
* @throws {Error} If the request fails or returns an error
|
|
71
|
+
*
|
|
72
|
+
* @example
|
|
73
|
+
* ```typescript
|
|
74
|
+
* // List projects
|
|
75
|
+
* const result = await client.call('list_projects');
|
|
76
|
+
*
|
|
77
|
+
* // Get a specific ticket
|
|
78
|
+
* const ticket = await client.call('get_ticket', { ticketId: 'ticket123' });
|
|
79
|
+
* ```
|
|
80
|
+
*/
|
|
81
|
+
call<T = unknown>(operation: string, args?: Record<string, unknown>, options?: ApiRequestOptions): Promise<T>;
|
|
82
|
+
/**
|
|
83
|
+
* Validate the configured API key
|
|
84
|
+
*
|
|
85
|
+
* Makes a lightweight call to verify the API key is valid and
|
|
86
|
+
* returns the associated user information.
|
|
87
|
+
*
|
|
88
|
+
* @returns Promise resolving to validation result
|
|
89
|
+
*
|
|
90
|
+
* @example
|
|
91
|
+
* ```typescript
|
|
92
|
+
* const result = await client.validateApiKey();
|
|
93
|
+
* if (result.valid) {
|
|
94
|
+
* console.log('Authenticated as:', result.userId);
|
|
95
|
+
* } else {
|
|
96
|
+
* console.error('Invalid API key:', result.message);
|
|
97
|
+
* }
|
|
98
|
+
* ```
|
|
99
|
+
*/
|
|
100
|
+
validateApiKey(): Promise<ApiKeyValidationResult>;
|
|
101
|
+
/**
|
|
102
|
+
* Get human-readable message for HTTP status codes
|
|
103
|
+
*/
|
|
104
|
+
private getStatusMessage;
|
|
105
|
+
/**
|
|
106
|
+
* Combine multiple AbortSignals into one
|
|
107
|
+
*/
|
|
108
|
+
private combineSignals;
|
|
109
|
+
}
|
|
110
|
+
//# sourceMappingURL=api-client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"api-client.d.ts","sourceRoot":"","sources":["../../src/client/api-client.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAE/C;;GAEG;AACH,MAAM,WAAW,WAAW,CAAC,CAAC,GAAG,OAAO;IACtC,+BAA+B;IAC/B,IAAI,CAAC,EAAE,CAAC,CAAC;IACT,+BAA+B;IAC/B,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;GAEG;AACH,MAAM,WAAW,sBAAsB;IACrC,mCAAmC;IACnC,KAAK,EAAE,OAAO,CAAC;IACf,iDAAiD;IACjD,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,gDAAgD;IAChD,WAAW,CAAC,EAAE;QACZ,IAAI,CAAC,EAAE,OAAO,CAAC;QACf,KAAK,CAAC,EAAE,OAAO,CAAC;QAChB,KAAK,CAAC,EAAE,OAAO,CAAC;KACjB,CAAC;IACF,iCAAiC;IACjC,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,sCAAsC;IACtC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,oCAAoC;IACpC,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,sCAAsC;IACtC,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB;AAOD;;;;;;;;;;;;;;GAcG;AACH,qBAAa,SAAS;IACpB,OAAO,CAAC,MAAM,CAAY;gBAEd,MAAM,EAAE,SAAS;IAI7B;;;;;;;;;;;;;;;;;OAiBG;IACG,IAAI,CAAC,CAAC,GAAG,OAAO,EACpB,SAAS,EAAE,MAAM,EACjB,IAAI,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,EAClC,OAAO,GAAE,iBAAsB,GAC9B,OAAO,CAAC,CAAC,CAAC;IAwEb;;;;;;;;;;;;;;;;;OAiBG;IACG,cAAc,IAAI,OAAO,CAAC,sBAAsB,CAAC;IAwBvD;;OAEG;IACH,OAAO,CAAC,gBAAgB;IAiBxB;;OAEG;IACH,OAAO,CAAC,cAAc;CAiBvB"}
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SpecForge API Client
|
|
3
|
+
*
|
|
4
|
+
* HTTP client for communicating with the SpecForge API Gateway.
|
|
5
|
+
* Handles authentication, request/response formatting, and error handling.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Default request timeout (30 seconds)
|
|
9
|
+
*/
|
|
10
|
+
const DEFAULT_TIMEOUT = 30000;
|
|
11
|
+
/**
|
|
12
|
+
* API Client for SpecForge
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* ```typescript
|
|
16
|
+
* import { getConfig } from './config/index.js';
|
|
17
|
+
* import { ApiClient } from './client/api-client.js';
|
|
18
|
+
*
|
|
19
|
+
* const config = getConfig();
|
|
20
|
+
* const client = new ApiClient(config);
|
|
21
|
+
*
|
|
22
|
+
* // Make an API call
|
|
23
|
+
* const projects = await client.call('list_projects', { userId: 'user123' });
|
|
24
|
+
* ```
|
|
25
|
+
*/
|
|
26
|
+
export class ApiClient {
|
|
27
|
+
config;
|
|
28
|
+
constructor(config) {
|
|
29
|
+
this.config = config;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Make an API call to SpecForge
|
|
33
|
+
*
|
|
34
|
+
* @param operation - Name of the operation to execute
|
|
35
|
+
* @param args - Arguments for the operation
|
|
36
|
+
* @param options - Optional request configuration
|
|
37
|
+
* @returns Promise resolving to the operation result
|
|
38
|
+
* @throws {Error} If the request fails or returns an error
|
|
39
|
+
*
|
|
40
|
+
* @example
|
|
41
|
+
* ```typescript
|
|
42
|
+
* // List projects
|
|
43
|
+
* const result = await client.call('list_projects');
|
|
44
|
+
*
|
|
45
|
+
* // Get a specific ticket
|
|
46
|
+
* const ticket = await client.call('get_ticket', { ticketId: 'ticket123' });
|
|
47
|
+
* ```
|
|
48
|
+
*/
|
|
49
|
+
async call(operation, args = {}, options = {}) {
|
|
50
|
+
const { timeout = DEFAULT_TIMEOUT, headers = {}, signal } = options;
|
|
51
|
+
// Create abort controller for timeout
|
|
52
|
+
const controller = new AbortController();
|
|
53
|
+
const timeoutId = setTimeout(() => controller.abort(), timeout);
|
|
54
|
+
// Combine signals if provided
|
|
55
|
+
const requestSignal = signal
|
|
56
|
+
? this.combineSignals(signal, controller.signal)
|
|
57
|
+
: controller.signal;
|
|
58
|
+
try {
|
|
59
|
+
if (this.config.debug) {
|
|
60
|
+
console.log(`[ApiClient] Calling operation: ${operation}`, {
|
|
61
|
+
args: Object.keys(args),
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
const response = await fetch(this.config.apiUrl, {
|
|
65
|
+
method: 'POST',
|
|
66
|
+
headers: {
|
|
67
|
+
'Content-Type': 'application/json',
|
|
68
|
+
Authorization: `Bearer ${this.config.apiKey}`,
|
|
69
|
+
...headers,
|
|
70
|
+
},
|
|
71
|
+
body: JSON.stringify({
|
|
72
|
+
operation,
|
|
73
|
+
...args,
|
|
74
|
+
}),
|
|
75
|
+
signal: requestSignal,
|
|
76
|
+
});
|
|
77
|
+
clearTimeout(timeoutId);
|
|
78
|
+
// Handle non-2xx responses
|
|
79
|
+
if (!response.ok) {
|
|
80
|
+
const errorText = await response.text();
|
|
81
|
+
const statusMessage = this.getStatusMessage(response.status);
|
|
82
|
+
throw new Error(`API request failed: ${response.status} ${statusMessage}\n${errorText}`);
|
|
83
|
+
}
|
|
84
|
+
// Parse response
|
|
85
|
+
const result = await response.json();
|
|
86
|
+
// Handle API-level errors
|
|
87
|
+
if (result.error) {
|
|
88
|
+
throw new Error(`Operation failed: ${result.error}`);
|
|
89
|
+
}
|
|
90
|
+
if (this.config.debug) {
|
|
91
|
+
console.log(`[ApiClient] Operation completed: ${operation}`);
|
|
92
|
+
}
|
|
93
|
+
return result.data;
|
|
94
|
+
}
|
|
95
|
+
catch (error) {
|
|
96
|
+
clearTimeout(timeoutId);
|
|
97
|
+
// Handle abort errors
|
|
98
|
+
if (error instanceof Error && error.name === 'AbortError') {
|
|
99
|
+
throw new Error(`Request timed out after ${timeout}ms for operation: ${operation}`);
|
|
100
|
+
}
|
|
101
|
+
throw error;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Validate the configured API key
|
|
106
|
+
*
|
|
107
|
+
* Makes a lightweight call to verify the API key is valid and
|
|
108
|
+
* returns the associated user information.
|
|
109
|
+
*
|
|
110
|
+
* @returns Promise resolving to validation result
|
|
111
|
+
*
|
|
112
|
+
* @example
|
|
113
|
+
* ```typescript
|
|
114
|
+
* const result = await client.validateApiKey();
|
|
115
|
+
* if (result.valid) {
|
|
116
|
+
* console.log('Authenticated as:', result.userId);
|
|
117
|
+
* } else {
|
|
118
|
+
* console.error('Invalid API key:', result.message);
|
|
119
|
+
* }
|
|
120
|
+
* ```
|
|
121
|
+
*/
|
|
122
|
+
async validateApiKey() {
|
|
123
|
+
try {
|
|
124
|
+
const result = await this.call('validate_api_key');
|
|
125
|
+
return {
|
|
126
|
+
valid: true,
|
|
127
|
+
userId: result.userId,
|
|
128
|
+
permissions: result.permissions,
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
catch (error) {
|
|
132
|
+
return {
|
|
133
|
+
valid: false,
|
|
134
|
+
message: error instanceof Error ? error.message : 'Unknown error',
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Get human-readable message for HTTP status codes
|
|
140
|
+
*/
|
|
141
|
+
getStatusMessage(status) {
|
|
142
|
+
const messages = {
|
|
143
|
+
400: 'Bad Request',
|
|
144
|
+
401: 'Unauthorized - Check your API key',
|
|
145
|
+
403: 'Forbidden - Insufficient permissions',
|
|
146
|
+
404: 'Not Found',
|
|
147
|
+
429: 'Too Many Requests - Rate limit exceeded',
|
|
148
|
+
500: 'Internal Server Error',
|
|
149
|
+
501: 'Not Implemented',
|
|
150
|
+
502: 'Bad Gateway',
|
|
151
|
+
503: 'Service Unavailable',
|
|
152
|
+
504: 'Gateway Timeout',
|
|
153
|
+
};
|
|
154
|
+
return messages[status] || 'Unknown Error';
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* Combine multiple AbortSignals into one
|
|
158
|
+
*/
|
|
159
|
+
combineSignals(signal1, signal2) {
|
|
160
|
+
const controller = new AbortController();
|
|
161
|
+
const abort = () => controller.abort();
|
|
162
|
+
signal1.addEventListener('abort', abort);
|
|
163
|
+
signal2.addEventListener('abort', abort);
|
|
164
|
+
if (signal1.aborted || signal2.aborted) {
|
|
165
|
+
controller.abort();
|
|
166
|
+
}
|
|
167
|
+
return controller.signal;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
//# sourceMappingURL=api-client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"api-client.js","sourceRoot":"","sources":["../../src/client/api-client.ts"],"names":[],"mappings":"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"}
|