@clavisagent/mcp-server 0.1.0 → 0.1.1

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 CHANGED
@@ -1,54 +1,91 @@
1
- # Clavis MCP Server
1
+ # @clavisagent/mcp-server
2
2
 
3
- Secure credential management for Claude Desktop and MCP servers.
3
+ MCP server for [Clavis](https://clavisagent.com) — secure credential management for Claude Desktop.
4
4
 
5
- ## Features
6
-
7
- - 🔐 Encrypted credential storage (AES-256)
8
- - 🔄 Automatic OAuth token refresh
9
- - ⚡ Distributed rate limiting
10
- - 📊 Full audit logging
5
+ Credentials are injected server-side. **Claude never sees raw API keys.**
11
6
 
12
7
  ## Installation
13
8
 
14
- ```bash
15
- npx @clavisagent/mcp-server
16
- ```
17
-
18
- Or install globally:
19
-
20
9
  ```bash
21
10
  npm install -g @clavisagent/mcp-server
22
11
  ```
23
12
 
24
- ## Usage with Claude Desktop
13
+ ## Claude Desktop Setup
25
14
 
26
- Add the following to your Claude Desktop configuration file (`claude_desktop_config.json`):
15
+ Add to your `claude_desktop_config.json`:
27
16
 
28
17
  ```json
29
18
  {
30
19
  "mcpServers": {
31
20
  "clavis": {
32
21
  "command": "npx",
33
- "args": ["-y", "@clavisagent/mcp-server"]
22
+ "args": ["-y", "@clavisagent/mcp-server"],
23
+ "env": {
24
+ "CLAVIS_API_KEY": "eyJ...",
25
+ "CLAVIS_API_URL": "https://your-clavis-instance.com"
26
+ }
34
27
  }
35
28
  }
36
29
  }
37
30
  ```
38
31
 
39
- ## Usage with Claude Code
32
+ Config file locations:
33
+ - **macOS:** `~/Library/Application Support/Claude/claude_desktop_config.json`
34
+ - **Windows:** `%APPDATA%\Claude\claude_desktop_config.json`
40
35
 
41
- ```bash
42
- claude mcp add clavis -- npx -y @clavisagent/mcp-server
36
+ Get your API key from `POST /v1/auth/login` — use the `access_token` (JWT), not the `cla_...` key shown at registration.
37
+
38
+ ## Tools
39
+
40
+ ### `list_services`
41
+ List all services with stored credentials.
42
+
43
+ ### `check_credential_status`
44
+ Check credential health without making any external API call.
45
+ ```
46
+ service_name: "stripe"
47
+ → Valid: yes, Status: healthy, Expires in: 58 minutes
48
+ ```
49
+
50
+ ### `call_service` ← Recommended
51
+ Make an API call with server-side credential injection. The credential never reaches Claude.
52
+ ```
53
+ service_name: "stripe"
54
+ method: "GET"
55
+ url: "https://api.stripe.com/v1/balance"
56
+ → Status: 200, Body: {"object": "balance", ...}
43
57
  ```
44
58
 
45
- ## Available Tools
59
+ ### `get_credentials` ← Legacy
60
+ Returns raw credential data. Use `call_service` instead — it keeps secrets out of the conversation entirely.
61
+
62
+ ## Self-Hosted Setup
63
+
64
+ If running Clavis locally:
65
+
66
+ ```json
67
+ {
68
+ "mcpServers": {
69
+ "clavis": {
70
+ "command": "node",
71
+ "args": ["/path/to/clavis-mcp/dist/index.js"],
72
+ "env": {
73
+ "CLAVIS_API_KEY": "eyJ...",
74
+ "CLAVIS_API_URL": "http://localhost:8000"
75
+ }
76
+ }
77
+ }
78
+ }
79
+ ```
80
+
81
+ ## Changelog
82
+
83
+ ### v0.1.1
84
+ - Added `call_service` tool (server-side credential injection)
85
+ - `call_service` is the recommended tool for prompt injection immunity
46
86
 
47
- | Tool | Description |
48
- |---|---|
49
- | `get_credentials` | Retrieve a valid access token or API key for a named service |
50
- | `list_services` | List all services with stored credentials |
51
- | `check_credential_status` | Check the status and expiry of credentials for a service |
87
+ ### v0.1.0
88
+ - Initial release: `list_services`, `check_credential_status`, `get_credentials`
52
89
 
53
90
  ## License
54
91
 
package/dist/index.d.ts CHANGED
@@ -2,9 +2,9 @@
2
2
  /**
3
3
  * Clavis MCP Server
4
4
  *
5
- * Exposes Clavis credential management to Claude Desktop and other MCP clients.
6
- * Configure via environment variable:
7
- * CLAVIS_API_KEY your Clavis JWT (from POST /v1/auth/login)
5
+ * Exposes Clavis credential management to Claude Desktop via the
6
+ * Model Context Protocol. Credentials are injected server-side —
7
+ * Claude never sees raw API keys.
8
8
  */
9
9
  export {};
10
10
  //# sourceMappingURL=index.d.ts.map
package/dist/index.js CHANGED
@@ -1,138 +1,274 @@
1
1
  #!/usr/bin/env node
2
+ "use strict";
2
3
  /**
3
4
  * Clavis MCP Server
4
5
  *
5
- * Exposes Clavis credential management to Claude Desktop and other MCP clients.
6
- * Configure via environment variable:
7
- * CLAVIS_API_KEY your Clavis JWT (from POST /v1/auth/login)
6
+ * Exposes Clavis credential management to Claude Desktop via the
7
+ * Model Context Protocol. Credentials are injected server-side —
8
+ * Claude never sees raw API keys.
8
9
  */
9
- import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
10
- import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
11
- import { z } from "zod";
10
+ Object.defineProperty(exports, "__esModule", { value: true });
11
+ const index_js_1 = require("@modelcontextprotocol/sdk/server/index.js");
12
+ const stdio_js_1 = require("@modelcontextprotocol/sdk/server/stdio.js");
13
+ const types_js_1 = require("@modelcontextprotocol/sdk/types.js");
12
14
  // ---------------------------------------------------------------------------
13
- // Config
15
+ // Config from environment
14
16
  // ---------------------------------------------------------------------------
15
- const BASE_URL = "https://clavisagent.com";
16
- const apiKey = process.env.CLAVIS_API_KEY;
17
- if (!apiKey) {
18
- console.error("[clavis-mcp] Error: CLAVIS_API_KEY environment variable is not set.");
19
- process.exit(1);
17
+ const CLAVIS_API_URL = (process.env.CLAVIS_API_URL ?? "http://localhost:8000").replace(/\/$/, "");
18
+ const CLAVIS_API_KEY = process.env.CLAVIS_API_KEY ?? "";
19
+ if (!CLAVIS_API_KEY) {
20
+ process.stderr.write("[clavis-mcp] WARNING: CLAVIS_API_KEY is not set. All requests will fail with 401.\n");
20
21
  }
21
- const authHeaders = {
22
- Authorization: `Bearer ${apiKey}`,
22
+ const AUTH_HEADERS = {
23
+ Authorization: `Bearer ${CLAVIS_API_KEY}`,
23
24
  "Content-Type": "application/json",
24
25
  };
26
+ // ---------------------------------------------------------------------------
27
+ // Clavis API helpers
28
+ // ---------------------------------------------------------------------------
25
29
  async function clavisGet(path) {
26
- const url = `${BASE_URL}${path}`;
27
- const res = await fetch(url, { headers: authHeaders });
30
+ const { default: fetch } = await import("node-fetch");
31
+ const url = `${CLAVIS_API_URL}${path}`;
32
+ const res = await fetch(url, { headers: AUTH_HEADERS });
33
+ const body = await res.json();
28
34
  if (!res.ok) {
29
- let detail = `HTTP ${res.status}`;
30
- try {
31
- const body = (await res.json());
32
- detail = body.detail ?? body.message ?? detail;
33
- }
34
- catch {
35
- // ignore parse error — use status code only
36
- }
37
- throw new Error(`Clavis API error on ${path}: ${detail}`);
35
+ const detail = body.detail ?? res.statusText;
36
+ throw new Error(`Clavis API error ${res.status}: ${detail}`);
38
37
  }
39
- return res.json();
38
+ return body;
39
+ }
40
+ async function clavisPost(path, payload) {
41
+ const { default: fetch } = await import("node-fetch");
42
+ const url = `${CLAVIS_API_URL}${path}`;
43
+ const res = await fetch(url, {
44
+ method: "POST",
45
+ headers: AUTH_HEADERS,
46
+ body: JSON.stringify(payload),
47
+ });
48
+ const body = await res.json();
49
+ if (!res.ok) {
50
+ const detail = body.detail ?? res.statusText;
51
+ throw new Error(`Clavis API error ${res.status}: ${detail}`);
52
+ }
53
+ return body;
40
54
  }
41
55
  // ---------------------------------------------------------------------------
42
- // MCP Server
56
+ // Tool definitions
43
57
  // ---------------------------------------------------------------------------
44
- const server = new McpServer({
45
- name: "clavis",
46
- version: "0.1.0",
47
- });
48
- // ── Tool 1: get_credentials ────────────────────────────────────────────────
49
- server.tool("get_credentials", "Retrieve a valid access token or API key for a named service from Clavis. " +
50
- "Clavis handles token refresh and rotation automatically.", {
51
- service_name: z
52
- .string()
53
- .min(1)
54
- .describe("The name of the service to retrieve credentials for (e.g. 'github', 'openai')"),
55
- }, async ({ service_name }) => {
56
- const data = await clavisGet(`/v1/tokens/${service_name}`);
57
- return {
58
- content: [
59
- {
60
- type: "text",
61
- text: JSON.stringify({
62
- service: service_name,
63
- access_token: data.access_token,
64
- token_type: data.token_type ?? "Bearer",
65
- ...(data.expires_in != null && { expires_in_seconds: data.expires_in }),
66
- }, null, 2),
58
+ const TOOLS = [
59
+ {
60
+ name: "list_services",
61
+ description: "List all services with stored credentials in Clavis. Returns service names and connector types.",
62
+ inputSchema: {
63
+ type: "object",
64
+ properties: {},
65
+ required: [],
66
+ },
67
+ },
68
+ {
69
+ name: "check_credential_status",
70
+ description: "Check credential health for a service without making any external API call. " +
71
+ "Returns validity, expiry, and rate-limit info. Safe to call frequently as a health check.",
72
+ inputSchema: {
73
+ type: "object",
74
+ properties: {
75
+ service_name: {
76
+ type: "string",
77
+ description: "Name of the service to check (e.g. 'openai', 'stripe')",
78
+ },
67
79
  },
68
- ],
69
- };
70
- });
71
- // ── Tool 2: list_services ─────────────────────────────────────────────────
72
- server.tool("list_services", "List all services configured in Clavis for the authenticated developer.", {}, async () => {
73
- const data = await clavisGet("/v1/services");
74
- // Normalise: API may return an array directly or wrapped in { services: [...] }
75
- const services = Array.isArray(data)
76
- ? data
77
- : data.services ?? [];
78
- const active = services.filter((s) => s.is_active);
79
- return {
80
- content: [
81
- {
82
- type: "text",
83
- text: JSON.stringify({
84
- total: services.length,
85
- active: active.length,
86
- services: services.map((s) => ({
87
- name: s.name,
88
- id: s.id,
89
- is_active: s.is_active,
90
- created_at: s.created_at,
91
- })),
92
- }, null, 2),
80
+ required: ["service_name"],
81
+ },
82
+ },
83
+ {
84
+ name: "call_service",
85
+ description: "RECOMMENDED: Make an API call with server-side credential injection. " +
86
+ "The credential is fetched from the Clavis vault and injected into the upstream request. " +
87
+ "Claude never sees the raw API key — this is the secure way to call external APIs.",
88
+ inputSchema: {
89
+ type: "object",
90
+ properties: {
91
+ service_name: {
92
+ type: "string",
93
+ description: "Name of the Clavis service whose credentials to inject (e.g. 'openai')",
94
+ },
95
+ method: {
96
+ type: "string",
97
+ enum: ["GET", "POST", "PUT", "PATCH", "DELETE"],
98
+ description: "HTTP method",
99
+ },
100
+ url: {
101
+ type: "string",
102
+ description: "Full URL to call. Must be on the service's allowed domain (SSRF protection).",
103
+ },
104
+ headers: {
105
+ type: "object",
106
+ description: "Additional request headers (optional)",
107
+ additionalProperties: { type: "string" },
108
+ },
109
+ params: {
110
+ type: "object",
111
+ description: "URL query parameters (optional)",
112
+ additionalProperties: true,
113
+ },
114
+ json: {
115
+ type: "object",
116
+ description: "JSON request body (optional, mutually exclusive with data)",
117
+ additionalProperties: true,
118
+ },
119
+ data: {
120
+ description: "Form-encoded body (optional, mutually exclusive with json)",
121
+ },
93
122
  },
94
- ],
95
- };
96
- });
97
- // ── Tool 3: check_credential_status ───────────────────────────────────────
98
- server.tool("check_credential_status", "Check the validity and rate-limit status of credentials for a service. " +
99
- "NOTE: The /v1/credentials/{service}/check endpoint is not yet implemented " +
100
- "returns a stub response until the API endpoint is live.", {
101
- service_name: z
102
- .string()
103
- .min(1)
104
- .describe("The name of the service to check (e.g. 'github', 'openai')"),
105
- }, async ({ service_name }) => {
106
- // Stub — endpoint /v1/credentials/{service_name}/check is not yet implemented.
107
- // When the endpoint goes live, replace the body below with:
108
- // const data = await clavisGet<CredentialStatus>(`/v1/credentials/${service_name}/check`);
109
- // return { content: [{ type: "text", text: JSON.stringify(data, null, 2) }] };
110
- const stub = {
111
- service_name,
112
- valid: true,
113
- expires_in: undefined,
114
- rate_limit_remaining: undefined,
115
- note: "check_credential_status is stubbed — /v1/credentials/{service}/check not yet implemented.",
116
- };
117
- return {
118
- content: [
119
- {
120
- type: "text",
121
- text: JSON.stringify(stub, null, 2),
123
+ required: ["service_name", "method", "url"],
124
+ },
125
+ },
126
+ {
127
+ name: "get_credentials",
128
+ description: "LEGACY returns raw credential data. Prefer call_service for security: " +
129
+ "call_service injects credentials server-side so Claude never sees raw keys. " +
130
+ "Use this only if you need the credential value directly.",
131
+ inputSchema: {
132
+ type: "object",
133
+ properties: {
134
+ service_name: {
135
+ type: "string",
136
+ description: "Name of the service (e.g. 'openai', 'stripe')",
137
+ },
122
138
  },
123
- ],
139
+ required: ["service_name"],
140
+ },
141
+ },
142
+ ];
143
+ // ---------------------------------------------------------------------------
144
+ // Tool handlers
145
+ // ---------------------------------------------------------------------------
146
+ async function handleListServices() {
147
+ const data = await clavisGet("/v1/services");
148
+ const items = Array.isArray(data)
149
+ ? data
150
+ : data.items
151
+ ?? data.services
152
+ ?? [];
153
+ if (items.length === 0) {
154
+ return "No services registered in Clavis yet. Add one via the Clavis API or dashboard.";
155
+ }
156
+ const lines = items.map((s) => {
157
+ const svc = s;
158
+ const name = svc.name ?? svc.service_name ?? "unknown";
159
+ const connector = svc.connector_name ? ` (${svc.connector_name})` : "";
160
+ return `- ${name}${connector}`;
161
+ });
162
+ return `${items.length} service(s) registered:\n${lines.join("\n")}`;
163
+ }
164
+ async function handleCheckCredentialStatus(serviceName) {
165
+ const data = await clavisGet(`/v1/credentials/${encodeURIComponent(serviceName)}/check`);
166
+ const lines = [
167
+ `Service: ${serviceName}`,
168
+ `Valid: ${data.valid ? "yes" : "no"}`,
169
+ `Status: ${data.status ?? "unknown"}`,
170
+ ];
171
+ if (data.expires_in != null) {
172
+ const mins = Math.floor(data.expires_in / 60);
173
+ lines.push(`Expires in: ${mins > 0 ? `${mins} minutes` : "< 1 minute"}`);
174
+ }
175
+ if (data.rate_limit_remaining != null) {
176
+ lines.push(`Rate limit remaining: ${data.rate_limit_remaining}`);
177
+ }
178
+ if (data.last_used) {
179
+ lines.push(`Last used: ${data.last_used}`);
180
+ }
181
+ if (!data.valid && data.error) {
182
+ lines.push(`Error: ${data.error}`);
183
+ }
184
+ return lines.join("\n");
185
+ }
186
+ async function handleCallService(args) {
187
+ const payload = {
188
+ method: args.method,
189
+ url: args.url,
190
+ headers: args.headers ?? null,
191
+ params: args.params ?? null,
192
+ json: args.json ?? null,
193
+ data: args.data ?? null,
124
194
  };
195
+ const result = await clavisPost(`/v1/call/${encodeURIComponent(args.service_name)}`, payload);
196
+ const bodyStr = typeof result.body === "string"
197
+ ? result.body
198
+ : JSON.stringify(result.body, null, 2);
199
+ return [`Status: ${result.status_code}`, `Body:\n${bodyStr}`].join("\n");
200
+ }
201
+ async function handleGetCredentials(serviceName) {
202
+ const data = await clavisGet(`/v1/credentials/${encodeURIComponent(serviceName)}`);
203
+ return ("WARNING: This returned raw credential data. " +
204
+ "For future calls, prefer call_service — it injects credentials server-side " +
205
+ "so this conversation never contains raw API keys.\n\n" +
206
+ JSON.stringify(data, null, 2));
207
+ }
208
+ // ---------------------------------------------------------------------------
209
+ // MCP Server
210
+ // ---------------------------------------------------------------------------
211
+ const server = new index_js_1.Server({ name: "clavis", version: "0.1.1" }, { capabilities: { tools: {} } });
212
+ server.setRequestHandler(types_js_1.ListToolsRequestSchema, async () => ({ tools: TOOLS }));
213
+ server.setRequestHandler(types_js_1.CallToolRequestSchema, async (request) => {
214
+ const { name, arguments: args } = request.params;
215
+ const a = (args ?? {});
216
+ try {
217
+ let text;
218
+ switch (name) {
219
+ case "list_services":
220
+ text = await handleListServices();
221
+ break;
222
+ case "check_credential_status":
223
+ if (typeof a.service_name !== "string")
224
+ throw new Error("service_name is required");
225
+ text = await handleCheckCredentialStatus(a.service_name);
226
+ break;
227
+ case "call_service":
228
+ if (typeof a.service_name !== "string")
229
+ throw new Error("service_name is required");
230
+ if (typeof a.method !== "string")
231
+ throw new Error("method is required");
232
+ if (typeof a.url !== "string")
233
+ throw new Error("url is required");
234
+ text = await handleCallService({
235
+ service_name: a.service_name,
236
+ method: a.method,
237
+ url: a.url,
238
+ headers: a.headers,
239
+ params: a.params,
240
+ json: a.json,
241
+ data: a.data,
242
+ });
243
+ break;
244
+ case "get_credentials":
245
+ if (typeof a.service_name !== "string")
246
+ throw new Error("service_name is required");
247
+ text = await handleGetCredentials(a.service_name);
248
+ break;
249
+ default:
250
+ throw new Error(`Unknown tool: ${name}`);
251
+ }
252
+ return { content: [{ type: "text", text }] };
253
+ }
254
+ catch (err) {
255
+ const message = err instanceof Error ? err.message : String(err);
256
+ return {
257
+ content: [{ type: "text", text: `Error: ${message}` }],
258
+ isError: true,
259
+ };
260
+ }
125
261
  });
126
262
  // ---------------------------------------------------------------------------
127
263
  // Start
128
264
  // ---------------------------------------------------------------------------
129
265
  async function main() {
130
- const transport = new StdioServerTransport();
266
+ const transport = new stdio_js_1.StdioServerTransport();
131
267
  await server.connect(transport);
132
- console.error("[clavis-mcp] Server running on stdio");
268
+ process.stderr.write(`[clavis-mcp] Server started. API: ${CLAVIS_API_URL}\n`);
133
269
  }
134
270
  main().catch((err) => {
135
- console.error("[clavis-mcp] Fatal error:", err);
271
+ process.stderr.write(`[clavis-mcp] Fatal: ${err}\n`);
136
272
  process.exit(1);
137
273
  });
138
274
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,48 +1,32 @@
1
1
  {
2
2
  "name": "@clavisagent/mcp-server",
3
- "version": "0.1.0",
4
- "description": "MCP server for secure credential management. Handles encrypted storage, auto token refresh, and rate limiting for Claude Desktop and AI agents.",
5
- "type": "module",
6
- "author": "Clavis",
7
- "license": "MIT",
8
- "homepage": "https://clavisagent.com",
9
- "keywords": [
10
- "mcp",
11
- "model-context-protocol",
12
- "claude",
13
- "anthropic",
14
- "credentials",
15
- "authentication",
16
- "api-keys",
17
- "oauth",
18
- "token-refresh",
19
- "rate-limiting",
20
- "ai-agents",
21
- "security"
22
- ],
3
+ "version": "0.1.1",
4
+ "description": "MCP server for Clavis — secure credential management for Claude Desktop",
5
+ "main": "dist/index.js",
23
6
  "bin": {
24
- "clavis-mcp": "./dist/index.js"
7
+ "clavis-mcp": "dist/index.js"
25
8
  },
26
- "main": "./dist/index.js",
27
- "files": [
28
- "dist/",
29
- "README.md",
30
- "LICENSE"
31
- ],
32
9
  "scripts": {
33
10
  "build": "tsc",
34
- "dev": "tsc --watch",
35
- "start": "node dist/index.js"
11
+ "start": "node dist/index.js",
12
+ "dev": "ts-node src/index.ts"
36
13
  },
37
14
  "dependencies": {
38
- "@modelcontextprotocol/sdk": "^1.9.0",
39
- "zod": "^3.23.8"
15
+ "@modelcontextprotocol/sdk": "^1.0.0",
16
+ "node-fetch": "^3.3.2"
40
17
  },
41
18
  "devDependencies": {
42
- "@types/node": "^22.0.0",
43
- "typescript": "^5.5.0"
19
+ "@types/node": "^20.0.0",
20
+ "typescript": "^5.4.0",
21
+ "ts-node": "^10.9.0"
44
22
  },
45
23
  "engines": {
46
24
  "node": ">=18"
47
- }
25
+ },
26
+ "license": "MIT",
27
+ "repository": {
28
+ "type": "git",
29
+ "url": "https://github.com/KN0WBOT/clavis"
30
+ },
31
+ "keywords": ["mcp", "clavis", "credentials", "claude", "ai-agents"]
48
32
  }
package/src/index.ts ADDED
@@ -0,0 +1,342 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Clavis MCP Server
4
+ *
5
+ * Exposes Clavis credential management to Claude Desktop via the
6
+ * Model Context Protocol. Credentials are injected server-side —
7
+ * Claude never sees raw API keys.
8
+ */
9
+
10
+ import { Server } from "@modelcontextprotocol/sdk/server/index.js";
11
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
12
+ import {
13
+ CallToolRequestSchema,
14
+ ListToolsRequestSchema,
15
+ Tool,
16
+ } from "@modelcontextprotocol/sdk/types.js";
17
+
18
+ // ---------------------------------------------------------------------------
19
+ // Config from environment
20
+ // ---------------------------------------------------------------------------
21
+
22
+ const CLAVIS_API_URL = (process.env.CLAVIS_API_URL ?? "http://localhost:8000").replace(/\/$/, "");
23
+ const CLAVIS_API_KEY = process.env.CLAVIS_API_KEY ?? "";
24
+
25
+ if (!CLAVIS_API_KEY) {
26
+ process.stderr.write(
27
+ "[clavis-mcp] WARNING: CLAVIS_API_KEY is not set. All requests will fail with 401.\n"
28
+ );
29
+ }
30
+
31
+ const AUTH_HEADERS: Record<string, string> = {
32
+ Authorization: `Bearer ${CLAVIS_API_KEY}`,
33
+ "Content-Type": "application/json",
34
+ };
35
+
36
+ // ---------------------------------------------------------------------------
37
+ // Clavis API helpers
38
+ // ---------------------------------------------------------------------------
39
+
40
+ async function clavisGet(path: string): Promise<unknown> {
41
+ const { default: fetch } = await import("node-fetch");
42
+ const url = `${CLAVIS_API_URL}${path}`;
43
+ const res = await fetch(url, { headers: AUTH_HEADERS });
44
+ const body = await res.json() as unknown;
45
+ if (!res.ok) {
46
+ const detail = (body as { detail?: string }).detail ?? res.statusText;
47
+ throw new Error(`Clavis API error ${res.status}: ${detail}`);
48
+ }
49
+ return body;
50
+ }
51
+
52
+ async function clavisPost(path: string, payload: unknown): Promise<unknown> {
53
+ const { default: fetch } = await import("node-fetch");
54
+ const url = `${CLAVIS_API_URL}${path}`;
55
+ const res = await fetch(url, {
56
+ method: "POST",
57
+ headers: AUTH_HEADERS,
58
+ body: JSON.stringify(payload),
59
+ });
60
+ const body = await res.json() as unknown;
61
+ if (!res.ok) {
62
+ const detail = (body as { detail?: string }).detail ?? res.statusText;
63
+ throw new Error(`Clavis API error ${res.status}: ${detail}`);
64
+ }
65
+ return body;
66
+ }
67
+
68
+ // ---------------------------------------------------------------------------
69
+ // Tool definitions
70
+ // ---------------------------------------------------------------------------
71
+
72
+ const TOOLS: Tool[] = [
73
+ {
74
+ name: "list_services",
75
+ description:
76
+ "List all services with stored credentials in Clavis. Returns service names and connector types.",
77
+ inputSchema: {
78
+ type: "object",
79
+ properties: {},
80
+ required: [],
81
+ },
82
+ },
83
+ {
84
+ name: "check_credential_status",
85
+ description:
86
+ "Check credential health for a service without making any external API call. " +
87
+ "Returns validity, expiry, and rate-limit info. Safe to call frequently as a health check.",
88
+ inputSchema: {
89
+ type: "object",
90
+ properties: {
91
+ service_name: {
92
+ type: "string",
93
+ description: "Name of the service to check (e.g. 'openai', 'stripe')",
94
+ },
95
+ },
96
+ required: ["service_name"],
97
+ },
98
+ },
99
+ {
100
+ name: "call_service",
101
+ description:
102
+ "RECOMMENDED: Make an API call with server-side credential injection. " +
103
+ "The credential is fetched from the Clavis vault and injected into the upstream request. " +
104
+ "Claude never sees the raw API key — this is the secure way to call external APIs.",
105
+ inputSchema: {
106
+ type: "object",
107
+ properties: {
108
+ service_name: {
109
+ type: "string",
110
+ description: "Name of the Clavis service whose credentials to inject (e.g. 'openai')",
111
+ },
112
+ method: {
113
+ type: "string",
114
+ enum: ["GET", "POST", "PUT", "PATCH", "DELETE"],
115
+ description: "HTTP method",
116
+ },
117
+ url: {
118
+ type: "string",
119
+ description:
120
+ "Full URL to call. Must be on the service's allowed domain (SSRF protection).",
121
+ },
122
+ headers: {
123
+ type: "object",
124
+ description: "Additional request headers (optional)",
125
+ additionalProperties: { type: "string" },
126
+ },
127
+ params: {
128
+ type: "object",
129
+ description: "URL query parameters (optional)",
130
+ additionalProperties: true,
131
+ },
132
+ json: {
133
+ type: "object",
134
+ description: "JSON request body (optional, mutually exclusive with data)",
135
+ additionalProperties: true,
136
+ },
137
+ data: {
138
+ description: "Form-encoded body (optional, mutually exclusive with json)",
139
+ },
140
+ },
141
+ required: ["service_name", "method", "url"],
142
+ },
143
+ },
144
+ {
145
+ name: "get_credentials",
146
+ description:
147
+ "LEGACY — returns raw credential data. Prefer call_service for security: " +
148
+ "call_service injects credentials server-side so Claude never sees raw keys. " +
149
+ "Use this only if you need the credential value directly.",
150
+ inputSchema: {
151
+ type: "object",
152
+ properties: {
153
+ service_name: {
154
+ type: "string",
155
+ description: "Name of the service (e.g. 'openai', 'stripe')",
156
+ },
157
+ },
158
+ required: ["service_name"],
159
+ },
160
+ },
161
+ ];
162
+
163
+ // ---------------------------------------------------------------------------
164
+ // Tool handlers
165
+ // ---------------------------------------------------------------------------
166
+
167
+ async function handleListServices(): Promise<string> {
168
+ const data = await clavisGet("/v1/services") as { items?: unknown[]; services?: unknown[] } | unknown[];
169
+ const items: unknown[] = Array.isArray(data)
170
+ ? data
171
+ : (data as { items?: unknown[]; services?: unknown[] }).items
172
+ ?? (data as { services?: unknown[] }).services
173
+ ?? [];
174
+
175
+ if (items.length === 0) {
176
+ return "No services registered in Clavis yet. Add one via the Clavis API or dashboard.";
177
+ }
178
+
179
+ const lines = items.map((s) => {
180
+ const svc = s as { name?: string; service_name?: string; connector_name?: string };
181
+ const name = svc.name ?? svc.service_name ?? "unknown";
182
+ const connector = svc.connector_name ? ` (${svc.connector_name})` : "";
183
+ return `- ${name}${connector}`;
184
+ });
185
+
186
+ return `${items.length} service(s) registered:\n${lines.join("\n")}`;
187
+ }
188
+
189
+ async function handleCheckCredentialStatus(serviceName: string): Promise<string> {
190
+ const data = await clavisGet(`/v1/credentials/${encodeURIComponent(serviceName)}/check`) as {
191
+ valid?: boolean;
192
+ status?: string;
193
+ expires_in?: number | null;
194
+ rate_limit_remaining?: number | null;
195
+ last_used?: string | null;
196
+ error?: string;
197
+ };
198
+
199
+ const lines: string[] = [
200
+ `Service: ${serviceName}`,
201
+ `Valid: ${data.valid ? "yes" : "no"}`,
202
+ `Status: ${data.status ?? "unknown"}`,
203
+ ];
204
+
205
+ if (data.expires_in != null) {
206
+ const mins = Math.floor(data.expires_in / 60);
207
+ lines.push(`Expires in: ${mins > 0 ? `${mins} minutes` : "< 1 minute"}`);
208
+ }
209
+ if (data.rate_limit_remaining != null) {
210
+ lines.push(`Rate limit remaining: ${data.rate_limit_remaining}`);
211
+ }
212
+ if (data.last_used) {
213
+ lines.push(`Last used: ${data.last_used}`);
214
+ }
215
+ if (!data.valid && data.error) {
216
+ lines.push(`Error: ${data.error}`);
217
+ }
218
+
219
+ return lines.join("\n");
220
+ }
221
+
222
+ async function handleCallService(args: {
223
+ service_name: string;
224
+ method: string;
225
+ url: string;
226
+ headers?: Record<string, string>;
227
+ params?: Record<string, unknown>;
228
+ json?: Record<string, unknown>;
229
+ data?: unknown;
230
+ }): Promise<string> {
231
+ const payload = {
232
+ method: args.method,
233
+ url: args.url,
234
+ headers: args.headers ?? null,
235
+ params: args.params ?? null,
236
+ json: args.json ?? null,
237
+ data: args.data ?? null,
238
+ };
239
+
240
+ const result = await clavisPost(
241
+ `/v1/call/${encodeURIComponent(args.service_name)}`,
242
+ payload
243
+ ) as {
244
+ status_code: number;
245
+ headers: Record<string, string>;
246
+ body: unknown;
247
+ };
248
+
249
+ const bodyStr =
250
+ typeof result.body === "string"
251
+ ? result.body
252
+ : JSON.stringify(result.body, null, 2);
253
+
254
+ return [`Status: ${result.status_code}`, `Body:\n${bodyStr}`].join("\n");
255
+ }
256
+
257
+ async function handleGetCredentials(serviceName: string): Promise<string> {
258
+ const data = await clavisGet(`/v1/credentials/${encodeURIComponent(serviceName)}`);
259
+ return (
260
+ "WARNING: This returned raw credential data. " +
261
+ "For future calls, prefer call_service — it injects credentials server-side " +
262
+ "so this conversation never contains raw API keys.\n\n" +
263
+ JSON.stringify(data, null, 2)
264
+ );
265
+ }
266
+
267
+ // ---------------------------------------------------------------------------
268
+ // MCP Server
269
+ // ---------------------------------------------------------------------------
270
+
271
+ const server = new Server(
272
+ { name: "clavis", version: "0.1.1" },
273
+ { capabilities: { tools: {} } }
274
+ );
275
+
276
+ server.setRequestHandler(ListToolsRequestSchema, async () => ({ tools: TOOLS }));
277
+
278
+ server.setRequestHandler(CallToolRequestSchema, async (request) => {
279
+ const { name, arguments: args } = request.params;
280
+ const a = (args ?? {}) as Record<string, unknown>;
281
+
282
+ try {
283
+ let text: string;
284
+
285
+ switch (name) {
286
+ case "list_services":
287
+ text = await handleListServices();
288
+ break;
289
+
290
+ case "check_credential_status":
291
+ if (typeof a.service_name !== "string") throw new Error("service_name is required");
292
+ text = await handleCheckCredentialStatus(a.service_name);
293
+ break;
294
+
295
+ case "call_service":
296
+ if (typeof a.service_name !== "string") throw new Error("service_name is required");
297
+ if (typeof a.method !== "string") throw new Error("method is required");
298
+ if (typeof a.url !== "string") throw new Error("url is required");
299
+ text = await handleCallService({
300
+ service_name: a.service_name,
301
+ method: a.method,
302
+ url: a.url,
303
+ headers: a.headers as Record<string, string> | undefined,
304
+ params: a.params as Record<string, unknown> | undefined,
305
+ json: a.json as Record<string, unknown> | undefined,
306
+ data: a.data,
307
+ });
308
+ break;
309
+
310
+ case "get_credentials":
311
+ if (typeof a.service_name !== "string") throw new Error("service_name is required");
312
+ text = await handleGetCredentials(a.service_name);
313
+ break;
314
+
315
+ default:
316
+ throw new Error(`Unknown tool: ${name}`);
317
+ }
318
+
319
+ return { content: [{ type: "text", text }] };
320
+ } catch (err) {
321
+ const message = err instanceof Error ? err.message : String(err);
322
+ return {
323
+ content: [{ type: "text", text: `Error: ${message}` }],
324
+ isError: true,
325
+ };
326
+ }
327
+ });
328
+
329
+ // ---------------------------------------------------------------------------
330
+ // Start
331
+ // ---------------------------------------------------------------------------
332
+
333
+ async function main(): Promise<void> {
334
+ const transport = new StdioServerTransport();
335
+ await server.connect(transport);
336
+ process.stderr.write(`[clavis-mcp] Server started. API: ${CLAVIS_API_URL}\n`);
337
+ }
338
+
339
+ main().catch((err) => {
340
+ process.stderr.write(`[clavis-mcp] Fatal: ${err}\n`);
341
+ process.exit(1);
342
+ });
package/tsconfig.json ADDED
@@ -0,0 +1,17 @@
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
+ "declaration": true,
12
+ "declarationMap": true,
13
+ "sourceMap": true
14
+ },
15
+ "include": ["src/**/*"],
16
+ "exclude": ["node_modules", "dist"]
17
+ }