clearance-mcp 1.0.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 +118 -0
- package/dist/client.d.ts +20 -0
- package/dist/client.js +68 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +264 -0
- package/dist/types.d.ts +67 -0
- package/dist/types.js +2 -0
- package/package.json +32 -0
- package/src/client.ts +90 -0
- package/src/index.ts +282 -0
- package/src/types.ts +78 -0
- package/tsconfig.json +15 -0
package/README.md
ADDED
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
# clearance-mcp
|
|
2
|
+
|
|
3
|
+
> Model Context Protocol server for [Clearance](https://clearance.nauti-labs.com) — the human approval API for AI agent commerce.
|
|
4
|
+
|
|
5
|
+
Stop letting agents move money you didn't approve. One API call. The human approves with one tap. Any service verifies the signed token.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## What This Is
|
|
10
|
+
|
|
11
|
+
An MCP server that exposes Clearance as a set of tools Claude (and other MCP-compatible clients) can call natively. Your AI agent can:
|
|
12
|
+
|
|
13
|
+
- Request human approval before spending money
|
|
14
|
+
- Check approval status
|
|
15
|
+
- Verify signed tokens
|
|
16
|
+
- Manage API keys and usage
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
## Installation
|
|
21
|
+
|
|
22
|
+
### Via npm (recommended)
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
npm install -g clearance-mcp
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
Or run directly via npx:
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
npx -y clearance-mcp
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
### Via GitHub (if npm not available)
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
npx -y github:Nauti-Labs/clearance-mcp
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
---
|
|
41
|
+
|
|
42
|
+
## Configuration
|
|
43
|
+
|
|
44
|
+
### Claude Desktop
|
|
45
|
+
|
|
46
|
+
Add to your Claude Desktop config (`~/Library/Application Support/Claude/claude_desktop_config.json`):
|
|
47
|
+
|
|
48
|
+
```json
|
|
49
|
+
{
|
|
50
|
+
"mcpServers": {
|
|
51
|
+
"clearance": {
|
|
52
|
+
"command": "npx",
|
|
53
|
+
"args": ["-y", "clearance-mcp"],
|
|
54
|
+
"env": {
|
|
55
|
+
"CLEARANCE_API_KEY": "clr_live_..."
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
### Environment Variables
|
|
63
|
+
|
|
64
|
+
| Variable | Required | Description |
|
|
65
|
+
|----------|----------|-------------|
|
|
66
|
+
| `CLEARANCE_API_KEY` | ✅ | Your Clearance API key (get free at clearance.nauti-labs.com) |
|
|
67
|
+
| `CLEARANCE_BASE_URL` | ❌ | Override API base URL (default: https://clearance.nauti-labs.com) |
|
|
68
|
+
|
|
69
|
+
---
|
|
70
|
+
|
|
71
|
+
## Available Tools
|
|
72
|
+
|
|
73
|
+
| Tool | Description |
|
|
74
|
+
|------|-------------|
|
|
75
|
+
| `clearances_create` | Create a new human approval request |
|
|
76
|
+
| `clearances_get` | Get a single clearance by ID |
|
|
77
|
+
| `clearances_list` | List all clearances with optional filtering |
|
|
78
|
+
| `clearances_approve` | Approve a pending clearance |
|
|
79
|
+
| `clearances_deny` | Deny a pending clearance |
|
|
80
|
+
| `verify_token` | Verify a signed approval token |
|
|
81
|
+
| `keys_create` | Create a new API key |
|
|
82
|
+
| `usage_get` | Check current usage and limits |
|
|
83
|
+
|
|
84
|
+
---
|
|
85
|
+
|
|
86
|
+
## Example Usage
|
|
87
|
+
|
|
88
|
+
In Claude, after the MCP server is connected:
|
|
89
|
+
|
|
90
|
+
> **You:** "I want to book a $500 flight. Can you ask for approval first?"
|
|
91
|
+
>
|
|
92
|
+
> **Claude:** *calls `clearances_create` with title="Book flight to Austin", budget_amount=500*
|
|
93
|
+
>
|
|
94
|
+
> **Claude:** "I've created an approval request. Here's the link: [Approve](https://clearance.nauti-labs.com/approve/clr_abc123). Once you approve, I'll proceed with the booking."
|
|
95
|
+
|
|
96
|
+
---
|
|
97
|
+
|
|
98
|
+
## Pricing
|
|
99
|
+
|
|
100
|
+
Clearance pricing:
|
|
101
|
+
|
|
102
|
+
- **Starter** — Free, 50 clearances/month
|
|
103
|
+
- **Pro** — $19/mo, 1,000 clearances/month
|
|
104
|
+
- **Scale** — $49/mo, 10,000 clearances/month
|
|
105
|
+
|
|
106
|
+
Get your free key at [clearance.nauti-labs.com](https://clearance.nauti-labs.com).
|
|
107
|
+
|
|
108
|
+
---
|
|
109
|
+
|
|
110
|
+
## Links
|
|
111
|
+
|
|
112
|
+
- **Product:** [clearance.nauti-labs.com](https://clearance.nauti-labs.com)
|
|
113
|
+
- **Company:** [nauti-labs.com](https://nauti-labs.com)
|
|
114
|
+
- **API Docs:** [clearance.nauti-labs.com/v1/docs](https://clearance.nauti-labs.com/v1/docs)
|
|
115
|
+
|
|
116
|
+
---
|
|
117
|
+
|
|
118
|
+
Built by [Nauti-Labs LLC](https://nauti-labs.com) · Mont Belvieu, TX
|
package/dist/client.d.ts
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { ClearanceRequest, ClearanceResponse, ClearanceListResponse, VerifyTokenResponse, KeyCreateRequest, KeyCreateResponse, UsageResponse } from "./types.js";
|
|
2
|
+
export declare class ClearanceClient {
|
|
3
|
+
private baseUrl;
|
|
4
|
+
private apiKey;
|
|
5
|
+
private timeoutMs;
|
|
6
|
+
constructor(apiKey: string, baseUrl?: string, timeoutMs?: number);
|
|
7
|
+
private request;
|
|
8
|
+
createClearance(data: ClearanceRequest): Promise<ClearanceResponse>;
|
|
9
|
+
getClearance(id: string): Promise<ClearanceResponse>;
|
|
10
|
+
listClearances(params?: {
|
|
11
|
+
status?: string;
|
|
12
|
+
limit?: number;
|
|
13
|
+
offset?: number;
|
|
14
|
+
}): Promise<ClearanceListResponse>;
|
|
15
|
+
approveClearance(id: string): Promise<ClearanceResponse>;
|
|
16
|
+
denyClearance(id: string, reason?: string): Promise<ClearanceResponse>;
|
|
17
|
+
verifyToken(token: string): Promise<VerifyTokenResponse>;
|
|
18
|
+
createKey(data: KeyCreateRequest): Promise<KeyCreateResponse>;
|
|
19
|
+
getUsage(): Promise<UsageResponse>;
|
|
20
|
+
}
|
package/dist/client.js
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
export class ClearanceClient {
|
|
2
|
+
baseUrl;
|
|
3
|
+
apiKey;
|
|
4
|
+
timeoutMs;
|
|
5
|
+
constructor(apiKey, baseUrl = "https://clearance.nauti-labs.com", timeoutMs = 10000) {
|
|
6
|
+
this.apiKey = apiKey;
|
|
7
|
+
this.baseUrl = baseUrl.replace(/\/$/, "");
|
|
8
|
+
this.timeoutMs = timeoutMs;
|
|
9
|
+
}
|
|
10
|
+
async request(method, path, body) {
|
|
11
|
+
const url = `${this.baseUrl}${path}`;
|
|
12
|
+
const controller = new AbortController();
|
|
13
|
+
const timeout = setTimeout(() => controller.abort(), this.timeoutMs);
|
|
14
|
+
try {
|
|
15
|
+
const response = await fetch(url, {
|
|
16
|
+
method,
|
|
17
|
+
headers: {
|
|
18
|
+
"Content-Type": "application/json",
|
|
19
|
+
"X-API-Key": this.apiKey,
|
|
20
|
+
},
|
|
21
|
+
body: body ? JSON.stringify(body) : undefined,
|
|
22
|
+
signal: controller.signal,
|
|
23
|
+
});
|
|
24
|
+
clearTimeout(timeout);
|
|
25
|
+
if (!response.ok) {
|
|
26
|
+
const text = await response.text();
|
|
27
|
+
throw new Error(`Clearance API error ${response.status}: ${text || response.statusText}`);
|
|
28
|
+
}
|
|
29
|
+
return (await response.json());
|
|
30
|
+
}
|
|
31
|
+
catch (err) {
|
|
32
|
+
clearTimeout(timeout);
|
|
33
|
+
throw err;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
async createClearance(data) {
|
|
37
|
+
return this.request("POST", "/v1/clearances", data);
|
|
38
|
+
}
|
|
39
|
+
async getClearance(id) {
|
|
40
|
+
return this.request("GET", `/v1/clearances/${encodeURIComponent(id)}`);
|
|
41
|
+
}
|
|
42
|
+
async listClearances(params) {
|
|
43
|
+
const query = new URLSearchParams();
|
|
44
|
+
if (params?.status)
|
|
45
|
+
query.set("status", params.status);
|
|
46
|
+
if (params?.limit !== undefined)
|
|
47
|
+
query.set("limit", String(params.limit));
|
|
48
|
+
if (params?.offset !== undefined)
|
|
49
|
+
query.set("offset", String(params.offset));
|
|
50
|
+
const qs = query.toString();
|
|
51
|
+
return this.request("GET", `/v1/clearances${qs ? "?" + qs : ""}`);
|
|
52
|
+
}
|
|
53
|
+
async approveClearance(id) {
|
|
54
|
+
return this.request("POST", `/v1/clearances/${encodeURIComponent(id)}/approve`);
|
|
55
|
+
}
|
|
56
|
+
async denyClearance(id, reason) {
|
|
57
|
+
return this.request("POST", `/v1/clearances/${encodeURIComponent(id)}/deny`, { reason });
|
|
58
|
+
}
|
|
59
|
+
async verifyToken(token) {
|
|
60
|
+
return this.request("GET", `/v1/verify/${encodeURIComponent(token)}`);
|
|
61
|
+
}
|
|
62
|
+
async createKey(data) {
|
|
63
|
+
return this.request("POST", "/v1/keys", data);
|
|
64
|
+
}
|
|
65
|
+
async getUsage() {
|
|
66
|
+
return this.request("GET", "/v1/usage");
|
|
67
|
+
}
|
|
68
|
+
}
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
3
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
4
|
+
import { CallToolRequestSchema, ListToolsRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
|
|
5
|
+
import { ClearanceClient } from "./client.js";
|
|
6
|
+
const API_KEY = process.env.CLEARANCE_API_KEY;
|
|
7
|
+
const BASE_URL = process.env.CLEARANCE_BASE_URL || "https://clearance.nauti-labs.com";
|
|
8
|
+
if (!API_KEY) {
|
|
9
|
+
console.error("CLEARANCE_API_KEY environment variable is required");
|
|
10
|
+
process.exit(1);
|
|
11
|
+
}
|
|
12
|
+
const client = new ClearanceClient(API_KEY, BASE_URL);
|
|
13
|
+
const TOOLS = [
|
|
14
|
+
{
|
|
15
|
+
name: "clearances_create",
|
|
16
|
+
description: "Create a new human approval request. The agent describes what it wants to do, the human gets a URL to approve or deny with one tap. Returns the clearance with an approve_url and deny_url.",
|
|
17
|
+
inputSchema: {
|
|
18
|
+
type: "object",
|
|
19
|
+
properties: {
|
|
20
|
+
title: {
|
|
21
|
+
type: "string",
|
|
22
|
+
description: "Human-readable description of what the agent wants to do",
|
|
23
|
+
},
|
|
24
|
+
scope: {
|
|
25
|
+
type: "string",
|
|
26
|
+
description: "Machine-readable scope tag (e.g. 'purchase:flight', 'transfer:usdc')",
|
|
27
|
+
},
|
|
28
|
+
budget_amount: {
|
|
29
|
+
type: "number",
|
|
30
|
+
description: "Maximum amount the agent is requesting to spend",
|
|
31
|
+
},
|
|
32
|
+
budget_currency: {
|
|
33
|
+
type: "string",
|
|
34
|
+
description: "Currency code (default: USD)",
|
|
35
|
+
default: "USD",
|
|
36
|
+
},
|
|
37
|
+
metadata: {
|
|
38
|
+
type: "object",
|
|
39
|
+
description: "Arbitrary key-value data attached to the clearance",
|
|
40
|
+
},
|
|
41
|
+
webhook_url: {
|
|
42
|
+
type: "string",
|
|
43
|
+
description: "URL to POST when the clearance is approved or denied",
|
|
44
|
+
},
|
|
45
|
+
},
|
|
46
|
+
required: ["title"],
|
|
47
|
+
},
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
name: "clearances_get",
|
|
51
|
+
description: "Get a single clearance by ID. Returns the full status, token, approval URL, and metadata.",
|
|
52
|
+
inputSchema: {
|
|
53
|
+
type: "object",
|
|
54
|
+
properties: {
|
|
55
|
+
id: {
|
|
56
|
+
type: "string",
|
|
57
|
+
description: "Clearance ID (e.g. clr_a1b2c3d4)",
|
|
58
|
+
},
|
|
59
|
+
},
|
|
60
|
+
required: ["id"],
|
|
61
|
+
},
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
name: "clearances_list",
|
|
65
|
+
description: "List all clearances with optional filtering by status, pagination.",
|
|
66
|
+
inputSchema: {
|
|
67
|
+
type: "object",
|
|
68
|
+
properties: {
|
|
69
|
+
status: {
|
|
70
|
+
type: "string",
|
|
71
|
+
description: "Filter by status: pending, approved, denied, revoked",
|
|
72
|
+
},
|
|
73
|
+
limit: {
|
|
74
|
+
type: "number",
|
|
75
|
+
description: "Max items per page (default: 20)",
|
|
76
|
+
default: 20,
|
|
77
|
+
},
|
|
78
|
+
offset: {
|
|
79
|
+
type: "number",
|
|
80
|
+
description: "Pagination offset (default: 0)",
|
|
81
|
+
default: 0,
|
|
82
|
+
},
|
|
83
|
+
},
|
|
84
|
+
required: [],
|
|
85
|
+
},
|
|
86
|
+
},
|
|
87
|
+
{
|
|
88
|
+
name: "clearances_approve",
|
|
89
|
+
description: "Approve a pending clearance by ID. This is typically done by the human, not the agent — but the API allows it for automated flows. Returns the updated clearance with a signed token.",
|
|
90
|
+
inputSchema: {
|
|
91
|
+
type: "object",
|
|
92
|
+
properties: {
|
|
93
|
+
id: {
|
|
94
|
+
type: "string",
|
|
95
|
+
description: "Clearance ID to approve",
|
|
96
|
+
},
|
|
97
|
+
},
|
|
98
|
+
required: ["id"],
|
|
99
|
+
},
|
|
100
|
+
},
|
|
101
|
+
{
|
|
102
|
+
name: "clearances_deny",
|
|
103
|
+
description: "Deny a pending clearance by ID. Optionally provide a reason. Returns the updated clearance.",
|
|
104
|
+
inputSchema: {
|
|
105
|
+
type: "object",
|
|
106
|
+
properties: {
|
|
107
|
+
id: {
|
|
108
|
+
type: "string",
|
|
109
|
+
description: "Clearance ID to deny",
|
|
110
|
+
},
|
|
111
|
+
reason: {
|
|
112
|
+
type: "string",
|
|
113
|
+
description: "Optional reason for denial",
|
|
114
|
+
},
|
|
115
|
+
},
|
|
116
|
+
required: ["id"],
|
|
117
|
+
},
|
|
118
|
+
},
|
|
119
|
+
{
|
|
120
|
+
name: "verify_token",
|
|
121
|
+
description: "Verify a signed clearance token. Any service can verify a token without authentication — this is how you prove a clearance was approved.",
|
|
122
|
+
inputSchema: {
|
|
123
|
+
type: "object",
|
|
124
|
+
properties: {
|
|
125
|
+
token: {
|
|
126
|
+
type: "string",
|
|
127
|
+
description: "The JWT token returned when a clearance was approved",
|
|
128
|
+
},
|
|
129
|
+
},
|
|
130
|
+
required: ["token"],
|
|
131
|
+
},
|
|
132
|
+
},
|
|
133
|
+
{
|
|
134
|
+
name: "keys_create",
|
|
135
|
+
description: "Create a new Clearance API key. You can request a specific tier (starter, pro, scale). Returns the new key and its limits.",
|
|
136
|
+
inputSchema: {
|
|
137
|
+
type: "object",
|
|
138
|
+
properties: {
|
|
139
|
+
tier: {
|
|
140
|
+
type: "string",
|
|
141
|
+
description: "Tier: starter (free), pro ($19/mo), scale ($49/mo)",
|
|
142
|
+
default: "starter",
|
|
143
|
+
},
|
|
144
|
+
email: {
|
|
145
|
+
type: "string",
|
|
146
|
+
description: "Optional email for key notifications",
|
|
147
|
+
},
|
|
148
|
+
},
|
|
149
|
+
required: [],
|
|
150
|
+
},
|
|
151
|
+
},
|
|
152
|
+
{
|
|
153
|
+
name: "usage_get",
|
|
154
|
+
description: "Get current usage and limits for this API key. Shows tier, monthly quota, used count, remaining count, and reset date.",
|
|
155
|
+
inputSchema: {
|
|
156
|
+
type: "object",
|
|
157
|
+
properties: {},
|
|
158
|
+
required: [],
|
|
159
|
+
},
|
|
160
|
+
},
|
|
161
|
+
];
|
|
162
|
+
const server = new Server({
|
|
163
|
+
name: "clearance-mcp",
|
|
164
|
+
version: "1.0.0",
|
|
165
|
+
}, {
|
|
166
|
+
capabilities: {
|
|
167
|
+
tools: {},
|
|
168
|
+
},
|
|
169
|
+
});
|
|
170
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
171
|
+
return { tools: TOOLS };
|
|
172
|
+
});
|
|
173
|
+
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
174
|
+
const { name, arguments: args } = request.params;
|
|
175
|
+
try {
|
|
176
|
+
let result;
|
|
177
|
+
switch (name) {
|
|
178
|
+
case "clearances_create": {
|
|
179
|
+
const { title, scope, budget_amount, budget_currency, metadata, webhook_url } = args;
|
|
180
|
+
result = await client.createClearance({
|
|
181
|
+
title: String(title),
|
|
182
|
+
scope: scope ? String(scope) : undefined,
|
|
183
|
+
budget_amount: typeof budget_amount === "number" ? budget_amount : undefined,
|
|
184
|
+
budget_currency: budget_currency ? String(budget_currency) : undefined,
|
|
185
|
+
metadata: typeof metadata === "object" && metadata !== null ? metadata : undefined,
|
|
186
|
+
webhook_url: webhook_url ? String(webhook_url) : undefined,
|
|
187
|
+
});
|
|
188
|
+
break;
|
|
189
|
+
}
|
|
190
|
+
case "clearances_get": {
|
|
191
|
+
const { id } = args;
|
|
192
|
+
result = await client.getClearance(id);
|
|
193
|
+
break;
|
|
194
|
+
}
|
|
195
|
+
case "clearances_list": {
|
|
196
|
+
const { status, limit, offset } = args;
|
|
197
|
+
result = await client.listClearances({
|
|
198
|
+
status: status ? String(status) : undefined,
|
|
199
|
+
limit: typeof limit === "number" ? limit : undefined,
|
|
200
|
+
offset: typeof offset === "number" ? offset : undefined,
|
|
201
|
+
});
|
|
202
|
+
break;
|
|
203
|
+
}
|
|
204
|
+
case "clearances_approve": {
|
|
205
|
+
const { id } = args;
|
|
206
|
+
result = await client.approveClearance(id);
|
|
207
|
+
break;
|
|
208
|
+
}
|
|
209
|
+
case "clearances_deny": {
|
|
210
|
+
const { id, reason } = args;
|
|
211
|
+
result = await client.denyClearance(id, reason);
|
|
212
|
+
break;
|
|
213
|
+
}
|
|
214
|
+
case "verify_token": {
|
|
215
|
+
const { token } = args;
|
|
216
|
+
result = await client.verifyToken(token);
|
|
217
|
+
break;
|
|
218
|
+
}
|
|
219
|
+
case "keys_create": {
|
|
220
|
+
const { tier, email } = args;
|
|
221
|
+
result = await client.createKey({
|
|
222
|
+
tier: tier ? String(tier) : undefined,
|
|
223
|
+
email: email ? String(email) : undefined,
|
|
224
|
+
});
|
|
225
|
+
break;
|
|
226
|
+
}
|
|
227
|
+
case "usage_get": {
|
|
228
|
+
result = await client.getUsage();
|
|
229
|
+
break;
|
|
230
|
+
}
|
|
231
|
+
default:
|
|
232
|
+
throw new Error(`Unknown tool: ${name}`);
|
|
233
|
+
}
|
|
234
|
+
return {
|
|
235
|
+
content: [
|
|
236
|
+
{
|
|
237
|
+
type: "text",
|
|
238
|
+
text: JSON.stringify(result, null, 2),
|
|
239
|
+
},
|
|
240
|
+
],
|
|
241
|
+
};
|
|
242
|
+
}
|
|
243
|
+
catch (err) {
|
|
244
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
245
|
+
return {
|
|
246
|
+
content: [
|
|
247
|
+
{
|
|
248
|
+
type: "text",
|
|
249
|
+
text: `Error: ${message}`,
|
|
250
|
+
},
|
|
251
|
+
],
|
|
252
|
+
isError: true,
|
|
253
|
+
};
|
|
254
|
+
}
|
|
255
|
+
});
|
|
256
|
+
async function main() {
|
|
257
|
+
const transport = new StdioServerTransport();
|
|
258
|
+
await server.connect(transport);
|
|
259
|
+
console.error("Clearance MCP server running on stdio");
|
|
260
|
+
}
|
|
261
|
+
main().catch((err) => {
|
|
262
|
+
console.error("Fatal error:", err);
|
|
263
|
+
process.exit(1);
|
|
264
|
+
});
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
export interface ClearanceRequest {
|
|
2
|
+
title: string;
|
|
3
|
+
scope?: string;
|
|
4
|
+
budget_amount?: number;
|
|
5
|
+
budget_currency?: string;
|
|
6
|
+
metadata?: Record<string, unknown>;
|
|
7
|
+
webhook_url?: string;
|
|
8
|
+
}
|
|
9
|
+
export interface ClearanceResponse {
|
|
10
|
+
id: string;
|
|
11
|
+
title: string;
|
|
12
|
+
scope: string | null;
|
|
13
|
+
budget_amount: number | null;
|
|
14
|
+
budget_currency: string | null;
|
|
15
|
+
status: "pending" | "approved" | "denied" | "revoked";
|
|
16
|
+
approve_url: string;
|
|
17
|
+
deny_url: string;
|
|
18
|
+
token: string | null;
|
|
19
|
+
metadata: Record<string, unknown> | null;
|
|
20
|
+
created_at: string;
|
|
21
|
+
approved_at: string | null;
|
|
22
|
+
denied_at: string | null;
|
|
23
|
+
denied_reason: string | null;
|
|
24
|
+
}
|
|
25
|
+
export interface ClearanceListResponse {
|
|
26
|
+
items: ClearanceResponse[];
|
|
27
|
+
total: number;
|
|
28
|
+
limit: number;
|
|
29
|
+
offset: number;
|
|
30
|
+
}
|
|
31
|
+
export interface VerifyTokenResponse {
|
|
32
|
+
valid: boolean;
|
|
33
|
+
clearance_id: string;
|
|
34
|
+
status: string;
|
|
35
|
+
created_at: string;
|
|
36
|
+
approved_at: string | null;
|
|
37
|
+
metadata: Record<string, unknown> | null;
|
|
38
|
+
}
|
|
39
|
+
export interface KeyCreateRequest {
|
|
40
|
+
tier?: string;
|
|
41
|
+
email?: string;
|
|
42
|
+
}
|
|
43
|
+
export interface KeyCreateResponse {
|
|
44
|
+
key: string;
|
|
45
|
+
tier: string;
|
|
46
|
+
clearances_per_month: number;
|
|
47
|
+
created_at: string;
|
|
48
|
+
}
|
|
49
|
+
export interface UsageResponse {
|
|
50
|
+
tier: string;
|
|
51
|
+
clearances_per_month: number;
|
|
52
|
+
used_this_month: number;
|
|
53
|
+
remaining_this_month: number;
|
|
54
|
+
reset_date: string;
|
|
55
|
+
}
|
|
56
|
+
export interface ClearanceApproveRequest {
|
|
57
|
+
id: string;
|
|
58
|
+
}
|
|
59
|
+
export interface ClearanceDenyRequest {
|
|
60
|
+
id: string;
|
|
61
|
+
reason?: string;
|
|
62
|
+
}
|
|
63
|
+
export interface ClearanceListRequest {
|
|
64
|
+
status?: string;
|
|
65
|
+
limit?: number;
|
|
66
|
+
offset?: number;
|
|
67
|
+
}
|
package/dist/types.js
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "clearance-mcp",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "MCP server for Clearance — human approval API for AI agent commerce",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"bin": {
|
|
8
|
+
"clearance-mcp": "dist/index.js"
|
|
9
|
+
},
|
|
10
|
+
"scripts": {
|
|
11
|
+
"build": "tsc",
|
|
12
|
+
"start": "node dist/index.js"
|
|
13
|
+
},
|
|
14
|
+
"keywords": [
|
|
15
|
+
"mcp",
|
|
16
|
+
"clearance",
|
|
17
|
+
"approval",
|
|
18
|
+
"agent",
|
|
19
|
+
"ai",
|
|
20
|
+
"payments"
|
|
21
|
+
],
|
|
22
|
+
"author": "Nauti-Labs LLC",
|
|
23
|
+
"license": "MIT",
|
|
24
|
+
"dependencies": {
|
|
25
|
+
"@modelcontextprotocol/sdk": "^1.0.0",
|
|
26
|
+
"zod": "^3.22.0"
|
|
27
|
+
},
|
|
28
|
+
"devDependencies": {
|
|
29
|
+
"@types/node": "^20.19.43",
|
|
30
|
+
"typescript": "^5.9.3"
|
|
31
|
+
}
|
|
32
|
+
}
|
package/src/client.ts
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import type { ClearanceRequest, ClearanceResponse, ClearanceListResponse, VerifyTokenResponse, KeyCreateRequest, KeyCreateResponse, UsageResponse } from "./types.js";
|
|
2
|
+
|
|
3
|
+
export class ClearanceClient {
|
|
4
|
+
private baseUrl: string;
|
|
5
|
+
private apiKey: string;
|
|
6
|
+
private timeoutMs: number;
|
|
7
|
+
|
|
8
|
+
constructor(apiKey: string, baseUrl = "https://clearance.nauti-labs.com", timeoutMs = 10000) {
|
|
9
|
+
this.apiKey = apiKey;
|
|
10
|
+
this.baseUrl = baseUrl.replace(/\/$/, "");
|
|
11
|
+
this.timeoutMs = timeoutMs;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
private async request<T>(
|
|
15
|
+
method: string,
|
|
16
|
+
path: string,
|
|
17
|
+
body?: unknown
|
|
18
|
+
): Promise<T> {
|
|
19
|
+
const url = `${this.baseUrl}${path}`;
|
|
20
|
+
const controller = new AbortController();
|
|
21
|
+
const timeout = setTimeout(() => controller.abort(), this.timeoutMs);
|
|
22
|
+
|
|
23
|
+
try {
|
|
24
|
+
const response = await fetch(url, {
|
|
25
|
+
method,
|
|
26
|
+
headers: {
|
|
27
|
+
"Content-Type": "application/json",
|
|
28
|
+
"X-API-Key": this.apiKey,
|
|
29
|
+
},
|
|
30
|
+
body: body ? JSON.stringify(body) : undefined,
|
|
31
|
+
signal: controller.signal,
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
clearTimeout(timeout);
|
|
35
|
+
|
|
36
|
+
if (!response.ok) {
|
|
37
|
+
const text = await response.text();
|
|
38
|
+
throw new Error(
|
|
39
|
+
`Clearance API error ${response.status}: ${text || response.statusText}`
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
return (await response.json()) as T;
|
|
44
|
+
} catch (err) {
|
|
45
|
+
clearTimeout(timeout);
|
|
46
|
+
throw err;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
async createClearance(data: ClearanceRequest): Promise<ClearanceResponse> {
|
|
51
|
+
return this.request<ClearanceResponse>("POST", "/v1/clearances", data);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
async getClearance(id: string): Promise<ClearanceResponse> {
|
|
55
|
+
return this.request<ClearanceResponse>("GET", `/v1/clearances/${encodeURIComponent(id)}`);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
async listClearances(params?: {
|
|
59
|
+
status?: string;
|
|
60
|
+
limit?: number;
|
|
61
|
+
offset?: number;
|
|
62
|
+
}): Promise<ClearanceListResponse> {
|
|
63
|
+
const query = new URLSearchParams();
|
|
64
|
+
if (params?.status) query.set("status", params.status);
|
|
65
|
+
if (params?.limit !== undefined) query.set("limit", String(params.limit));
|
|
66
|
+
if (params?.offset !== undefined) query.set("offset", String(params.offset));
|
|
67
|
+
const qs = query.toString();
|
|
68
|
+
return this.request<ClearanceListResponse>("GET", `/v1/clearances${qs ? "?" + qs : ""}`);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
async approveClearance(id: string): Promise<ClearanceResponse> {
|
|
72
|
+
return this.request<ClearanceResponse>("POST", `/v1/clearances/${encodeURIComponent(id)}/approve`);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
async denyClearance(id: string, reason?: string): Promise<ClearanceResponse> {
|
|
76
|
+
return this.request<ClearanceResponse>("POST", `/v1/clearances/${encodeURIComponent(id)}/deny`, { reason });
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
async verifyToken(token: string): Promise<VerifyTokenResponse> {
|
|
80
|
+
return this.request<VerifyTokenResponse>("GET", `/v1/verify/${encodeURIComponent(token)}`);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
async createKey(data: KeyCreateRequest): Promise<KeyCreateResponse> {
|
|
84
|
+
return this.request<KeyCreateResponse>("POST", "/v1/keys", data);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
async getUsage(): Promise<UsageResponse> {
|
|
88
|
+
return this.request<UsageResponse>("GET", "/v1/usage");
|
|
89
|
+
}
|
|
90
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,282 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
3
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
4
|
+
import {
|
|
5
|
+
CallToolRequestSchema,
|
|
6
|
+
ListToolsRequestSchema,
|
|
7
|
+
type Tool,
|
|
8
|
+
} from "@modelcontextprotocol/sdk/types.js";
|
|
9
|
+
import { ClearanceClient } from "./client.js";
|
|
10
|
+
|
|
11
|
+
const API_KEY = process.env.CLEARANCE_API_KEY;
|
|
12
|
+
const BASE_URL = process.env.CLEARANCE_BASE_URL || "https://clearance.nauti-labs.com";
|
|
13
|
+
|
|
14
|
+
if (!API_KEY) {
|
|
15
|
+
console.error("CLEARANCE_API_KEY environment variable is required");
|
|
16
|
+
process.exit(1);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const client = new ClearanceClient(API_KEY, BASE_URL);
|
|
20
|
+
|
|
21
|
+
const TOOLS: Tool[] = [
|
|
22
|
+
{
|
|
23
|
+
name: "clearances_create",
|
|
24
|
+
description: "Create a new human approval request. The agent describes what it wants to do, the human gets a URL to approve or deny with one tap. Returns the clearance with an approve_url and deny_url.",
|
|
25
|
+
inputSchema: {
|
|
26
|
+
type: "object",
|
|
27
|
+
properties: {
|
|
28
|
+
title: {
|
|
29
|
+
type: "string",
|
|
30
|
+
description: "Human-readable description of what the agent wants to do",
|
|
31
|
+
},
|
|
32
|
+
scope: {
|
|
33
|
+
type: "string",
|
|
34
|
+
description: "Machine-readable scope tag (e.g. 'purchase:flight', 'transfer:usdc')",
|
|
35
|
+
},
|
|
36
|
+
budget_amount: {
|
|
37
|
+
type: "number",
|
|
38
|
+
description: "Maximum amount the agent is requesting to spend",
|
|
39
|
+
},
|
|
40
|
+
budget_currency: {
|
|
41
|
+
type: "string",
|
|
42
|
+
description: "Currency code (default: USD)",
|
|
43
|
+
default: "USD",
|
|
44
|
+
},
|
|
45
|
+
metadata: {
|
|
46
|
+
type: "object",
|
|
47
|
+
description: "Arbitrary key-value data attached to the clearance",
|
|
48
|
+
},
|
|
49
|
+
webhook_url: {
|
|
50
|
+
type: "string",
|
|
51
|
+
description: "URL to POST when the clearance is approved or denied",
|
|
52
|
+
},
|
|
53
|
+
},
|
|
54
|
+
required: ["title"],
|
|
55
|
+
},
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
name: "clearances_get",
|
|
59
|
+
description: "Get a single clearance by ID. Returns the full status, token, approval URL, and metadata.",
|
|
60
|
+
inputSchema: {
|
|
61
|
+
type: "object",
|
|
62
|
+
properties: {
|
|
63
|
+
id: {
|
|
64
|
+
type: "string",
|
|
65
|
+
description: "Clearance ID (e.g. clr_a1b2c3d4)",
|
|
66
|
+
},
|
|
67
|
+
},
|
|
68
|
+
required: ["id"],
|
|
69
|
+
},
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
name: "clearances_list",
|
|
73
|
+
description: "List all clearances with optional filtering by status, pagination.",
|
|
74
|
+
inputSchema: {
|
|
75
|
+
type: "object",
|
|
76
|
+
properties: {
|
|
77
|
+
status: {
|
|
78
|
+
type: "string",
|
|
79
|
+
description: "Filter by status: pending, approved, denied, revoked",
|
|
80
|
+
},
|
|
81
|
+
limit: {
|
|
82
|
+
type: "number",
|
|
83
|
+
description: "Max items per page (default: 20)",
|
|
84
|
+
default: 20,
|
|
85
|
+
},
|
|
86
|
+
offset: {
|
|
87
|
+
type: "number",
|
|
88
|
+
description: "Pagination offset (default: 0)",
|
|
89
|
+
default: 0,
|
|
90
|
+
},
|
|
91
|
+
},
|
|
92
|
+
required: [],
|
|
93
|
+
},
|
|
94
|
+
},
|
|
95
|
+
{
|
|
96
|
+
name: "clearances_approve",
|
|
97
|
+
description: "Approve a pending clearance by ID. This is typically done by the human, not the agent — but the API allows it for automated flows. Returns the updated clearance with a signed token.",
|
|
98
|
+
inputSchema: {
|
|
99
|
+
type: "object",
|
|
100
|
+
properties: {
|
|
101
|
+
id: {
|
|
102
|
+
type: "string",
|
|
103
|
+
description: "Clearance ID to approve",
|
|
104
|
+
},
|
|
105
|
+
},
|
|
106
|
+
required: ["id"],
|
|
107
|
+
},
|
|
108
|
+
},
|
|
109
|
+
{
|
|
110
|
+
name: "clearances_deny",
|
|
111
|
+
description: "Deny a pending clearance by ID. Optionally provide a reason. Returns the updated clearance.",
|
|
112
|
+
inputSchema: {
|
|
113
|
+
type: "object",
|
|
114
|
+
properties: {
|
|
115
|
+
id: {
|
|
116
|
+
type: "string",
|
|
117
|
+
description: "Clearance ID to deny",
|
|
118
|
+
},
|
|
119
|
+
reason: {
|
|
120
|
+
type: "string",
|
|
121
|
+
description: "Optional reason for denial",
|
|
122
|
+
},
|
|
123
|
+
},
|
|
124
|
+
required: ["id"],
|
|
125
|
+
},
|
|
126
|
+
},
|
|
127
|
+
{
|
|
128
|
+
name: "verify_token",
|
|
129
|
+
description: "Verify a signed clearance token. Any service can verify a token without authentication — this is how you prove a clearance was approved.",
|
|
130
|
+
inputSchema: {
|
|
131
|
+
type: "object",
|
|
132
|
+
properties: {
|
|
133
|
+
token: {
|
|
134
|
+
type: "string",
|
|
135
|
+
description: "The JWT token returned when a clearance was approved",
|
|
136
|
+
},
|
|
137
|
+
},
|
|
138
|
+
required: ["token"],
|
|
139
|
+
},
|
|
140
|
+
},
|
|
141
|
+
{
|
|
142
|
+
name: "keys_create",
|
|
143
|
+
description: "Create a new Clearance API key. You can request a specific tier (starter, pro, scale). Returns the new key and its limits.",
|
|
144
|
+
inputSchema: {
|
|
145
|
+
type: "object",
|
|
146
|
+
properties: {
|
|
147
|
+
tier: {
|
|
148
|
+
type: "string",
|
|
149
|
+
description: "Tier: starter (free), pro ($19/mo), scale ($49/mo)",
|
|
150
|
+
default: "starter",
|
|
151
|
+
},
|
|
152
|
+
email: {
|
|
153
|
+
type: "string",
|
|
154
|
+
description: "Optional email for key notifications",
|
|
155
|
+
},
|
|
156
|
+
},
|
|
157
|
+
required: [],
|
|
158
|
+
},
|
|
159
|
+
},
|
|
160
|
+
{
|
|
161
|
+
name: "usage_get",
|
|
162
|
+
description: "Get current usage and limits for this API key. Shows tier, monthly quota, used count, remaining count, and reset date.",
|
|
163
|
+
inputSchema: {
|
|
164
|
+
type: "object",
|
|
165
|
+
properties: {},
|
|
166
|
+
required: [],
|
|
167
|
+
},
|
|
168
|
+
},
|
|
169
|
+
];
|
|
170
|
+
|
|
171
|
+
const server = new Server(
|
|
172
|
+
{
|
|
173
|
+
name: "clearance-mcp",
|
|
174
|
+
version: "1.0.0",
|
|
175
|
+
},
|
|
176
|
+
{
|
|
177
|
+
capabilities: {
|
|
178
|
+
tools: {},
|
|
179
|
+
},
|
|
180
|
+
}
|
|
181
|
+
);
|
|
182
|
+
|
|
183
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
184
|
+
return { tools: TOOLS };
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
188
|
+
const { name, arguments: args } = request.params;
|
|
189
|
+
|
|
190
|
+
try {
|
|
191
|
+
let result: unknown;
|
|
192
|
+
|
|
193
|
+
switch (name) {
|
|
194
|
+
case "clearances_create": {
|
|
195
|
+
const { title, scope, budget_amount, budget_currency, metadata, webhook_url } = args as Record<string, unknown>;
|
|
196
|
+
result = await client.createClearance({
|
|
197
|
+
title: String(title),
|
|
198
|
+
scope: scope ? String(scope) : undefined,
|
|
199
|
+
budget_amount: typeof budget_amount === "number" ? budget_amount : undefined,
|
|
200
|
+
budget_currency: budget_currency ? String(budget_currency) : undefined,
|
|
201
|
+
metadata: typeof metadata === "object" && metadata !== null ? (metadata as Record<string, unknown>) : undefined,
|
|
202
|
+
webhook_url: webhook_url ? String(webhook_url) : undefined,
|
|
203
|
+
});
|
|
204
|
+
break;
|
|
205
|
+
}
|
|
206
|
+
case "clearances_get": {
|
|
207
|
+
const { id } = args as Record<string, string>;
|
|
208
|
+
result = await client.getClearance(id);
|
|
209
|
+
break;
|
|
210
|
+
}
|
|
211
|
+
case "clearances_list": {
|
|
212
|
+
const { status, limit, offset } = args as Record<string, unknown>;
|
|
213
|
+
result = await client.listClearances({
|
|
214
|
+
status: status ? String(status) : undefined,
|
|
215
|
+
limit: typeof limit === "number" ? limit : undefined,
|
|
216
|
+
offset: typeof offset === "number" ? offset : undefined,
|
|
217
|
+
});
|
|
218
|
+
break;
|
|
219
|
+
}
|
|
220
|
+
case "clearances_approve": {
|
|
221
|
+
const { id } = args as Record<string, string>;
|
|
222
|
+
result = await client.approveClearance(id);
|
|
223
|
+
break;
|
|
224
|
+
}
|
|
225
|
+
case "clearances_deny": {
|
|
226
|
+
const { id, reason } = args as Record<string, string>;
|
|
227
|
+
result = await client.denyClearance(id, reason);
|
|
228
|
+
break;
|
|
229
|
+
}
|
|
230
|
+
case "verify_token": {
|
|
231
|
+
const { token } = args as Record<string, string>;
|
|
232
|
+
result = await client.verifyToken(token);
|
|
233
|
+
break;
|
|
234
|
+
}
|
|
235
|
+
case "keys_create": {
|
|
236
|
+
const { tier, email } = args as Record<string, unknown>;
|
|
237
|
+
result = await client.createKey({
|
|
238
|
+
tier: tier ? String(tier) : undefined,
|
|
239
|
+
email: email ? String(email) : undefined,
|
|
240
|
+
});
|
|
241
|
+
break;
|
|
242
|
+
}
|
|
243
|
+
case "usage_get": {
|
|
244
|
+
result = await client.getUsage();
|
|
245
|
+
break;
|
|
246
|
+
}
|
|
247
|
+
default:
|
|
248
|
+
throw new Error(`Unknown tool: ${name}`);
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
return {
|
|
252
|
+
content: [
|
|
253
|
+
{
|
|
254
|
+
type: "text",
|
|
255
|
+
text: JSON.stringify(result, null, 2),
|
|
256
|
+
},
|
|
257
|
+
],
|
|
258
|
+
};
|
|
259
|
+
} catch (err) {
|
|
260
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
261
|
+
return {
|
|
262
|
+
content: [
|
|
263
|
+
{
|
|
264
|
+
type: "text",
|
|
265
|
+
text: `Error: ${message}`,
|
|
266
|
+
},
|
|
267
|
+
],
|
|
268
|
+
isError: true,
|
|
269
|
+
};
|
|
270
|
+
}
|
|
271
|
+
});
|
|
272
|
+
|
|
273
|
+
async function main() {
|
|
274
|
+
const transport = new StdioServerTransport();
|
|
275
|
+
await server.connect(transport);
|
|
276
|
+
console.error("Clearance MCP server running on stdio");
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
main().catch((err) => {
|
|
280
|
+
console.error("Fatal error:", err);
|
|
281
|
+
process.exit(1);
|
|
282
|
+
});
|
package/src/types.ts
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
// Clearance API types
|
|
2
|
+
|
|
3
|
+
export interface ClearanceRequest {
|
|
4
|
+
title: string;
|
|
5
|
+
scope?: string;
|
|
6
|
+
budget_amount?: number;
|
|
7
|
+
budget_currency?: string;
|
|
8
|
+
metadata?: Record<string, unknown>;
|
|
9
|
+
webhook_url?: string;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export interface ClearanceResponse {
|
|
13
|
+
id: string;
|
|
14
|
+
title: string;
|
|
15
|
+
scope: string | null;
|
|
16
|
+
budget_amount: number | null;
|
|
17
|
+
budget_currency: string | null;
|
|
18
|
+
status: "pending" | "approved" | "denied" | "revoked";
|
|
19
|
+
approve_url: string;
|
|
20
|
+
deny_url: string;
|
|
21
|
+
token: string | null;
|
|
22
|
+
metadata: Record<string, unknown> | null;
|
|
23
|
+
created_at: string;
|
|
24
|
+
approved_at: string | null;
|
|
25
|
+
denied_at: string | null;
|
|
26
|
+
denied_reason: string | null;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export interface ClearanceListResponse {
|
|
30
|
+
items: ClearanceResponse[];
|
|
31
|
+
total: number;
|
|
32
|
+
limit: number;
|
|
33
|
+
offset: number;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export interface VerifyTokenResponse {
|
|
37
|
+
valid: boolean;
|
|
38
|
+
clearance_id: string;
|
|
39
|
+
status: string;
|
|
40
|
+
created_at: string;
|
|
41
|
+
approved_at: string | null;
|
|
42
|
+
metadata: Record<string, unknown> | null;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export interface KeyCreateRequest {
|
|
46
|
+
tier?: string;
|
|
47
|
+
email?: string;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export interface KeyCreateResponse {
|
|
51
|
+
key: string;
|
|
52
|
+
tier: string;
|
|
53
|
+
clearances_per_month: number;
|
|
54
|
+
created_at: string;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export interface UsageResponse {
|
|
58
|
+
tier: string;
|
|
59
|
+
clearances_per_month: number;
|
|
60
|
+
used_this_month: number;
|
|
61
|
+
remaining_this_month: number;
|
|
62
|
+
reset_date: string;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export interface ClearanceApproveRequest {
|
|
66
|
+
id: string;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export interface ClearanceDenyRequest {
|
|
70
|
+
id: string;
|
|
71
|
+
reason?: string;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export interface ClearanceListRequest {
|
|
75
|
+
status?: string;
|
|
76
|
+
limit?: number;
|
|
77
|
+
offset?: number;
|
|
78
|
+
}
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2022",
|
|
4
|
+
"module": "Node16",
|
|
5
|
+
"moduleResolution": "Node16",
|
|
6
|
+
"outDir": "./dist",
|
|
7
|
+
"rootDir": "./src",
|
|
8
|
+
"strict": true,
|
|
9
|
+
"esModuleInterop": true,
|
|
10
|
+
"skipLibCheck": true,
|
|
11
|
+
"forceConsistentCasingInFileNames": true,
|
|
12
|
+
"declaration": true
|
|
13
|
+
},
|
|
14
|
+
"include": ["src/**/*"]
|
|
15
|
+
}
|