starkgate-mcp-server 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,116 @@
1
+ # 🛡️ starkgate-mcp-server
2
+
3
+ [![npm](https://img.shields.io/npm/v/starkgate-mcp-server)](https://www.npmjs.com/package/starkgate-mcp-server)
4
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
5
+
6
+ **MCP server** ([Model Context Protocol](https://modelcontextprotocol.io)) qui fait passer les appels d'outils de tes agents IA par le firewall universel **StarkGate** — évaluation **fail-closed**, preuve `auditHash` vérifiable hors-ligne.
7
+
8
+ ## Installation
9
+
10
+ Aucune installation nécessaire avec npx :
11
+
12
+ ```bash
13
+ npx starkgate-mcp-server
14
+ ```
15
+
16
+ Ou en global :
17
+
18
+ ```bash
19
+ npm install -g starkgate-mcp-server
20
+ ```
21
+
22
+ ## Configuration (Claude Desktop & autres clients MCP)
23
+
24
+ Ajoute Ă  ton `claude_desktop_config.json` :
25
+
26
+ ```json
27
+ {
28
+ "mcpServers": {
29
+ "starkgate": {
30
+ "command": "npx",
31
+ "args": ["-y", "starkgate-mcp-server"],
32
+ "env": {
33
+ "STARKGATE_API_KEY": "sk_live_votre_cle",
34
+ "STARKGATE_API_URL": "https://sentinel-api.wenjoseph16.workers.dev",
35
+ "STARKGATE_POLICY_ID": "pol-demo",
36
+ "STARKGATE_AGENT_ID": "claude-desktop"
37
+ }
38
+ }
39
+ }
40
+ }
41
+ ```
42
+
43
+ | Variable | Requis | Défaut | Description |
44
+ |---|---|---|---|
45
+ | `STARKGATE_API_KEY` | pour evaluate | — | Clé API (`sk_live_…`) — sans elle, `starkgate_evaluate` répond DENY fail-closed |
46
+ | `STARKGATE_API_URL` | non | `https://sentinel-api.wenjoseph16.workers.dev` | URL de l'API StarkGate |
47
+ | `STARKGATE_POLICY_ID` | non | `pol-demo` | Policy par défaut |
48
+ | `STARKGATE_AGENT_ID` | non | `mcp-agent` | Identifiant de l'agent envoyé aux évaluations |
49
+
50
+ ## Tools exposés
51
+
52
+ ### `starkgate_evaluate`
53
+
54
+ Évalue une action avant exécution :
55
+
56
+ ```json
57
+ {
58
+ "actionType": "terminal.command",
59
+ "payload": { "cmd": "rm -rf /" },
60
+ "agentId": "mon-agent",
61
+ "policyId": "pol-demo"
62
+ }
63
+ ```
64
+
65
+ Réponse :
66
+
67
+ ```json
68
+ {
69
+ "decision": "DENY",
70
+ "reason": "Destructive command blocked by rule 'no-rm-rf'",
71
+ "matchedRule": "no-rm-rf",
72
+ "auditHash": "sha256:9f2c…",
73
+ "riskScore": 95
74
+ }
75
+ ```
76
+
77
+ - **Fail-closed** : erreur réseau/HTTP → `DENY` (jamais de fail-open silencieux).
78
+ - L'`auditHash` permet de **vérifier la preuve hors-ligne** (voir SDK Python `starkgate.local_audit`).
79
+
80
+ ### `starkgate_health`
81
+
82
+ Ping l'API (aucune clé requise) → `{"status": "ok", "apiUrl": "…"}`.
83
+
84
+ ## Usage programmatique (librairie)
85
+
86
+ Pour wrapper vos propres tools MCP avec le guard StarkGate :
87
+
88
+ ```typescript
89
+ import { createStarkGateMcpServer } from "starkgate-mcp-server";
90
+
91
+ const server = createStarkGateMcpServer({
92
+ apiUrl: "https://sentinel-api.wenjoseph16.workers.dev",
93
+ apiKey: "sk_live_…",
94
+ policyId: "pol-demo",
95
+ });
96
+
97
+ server.wrapTool({
98
+ name: "read_file",
99
+ description: "Read a file from the filesystem",
100
+ inputSchema: { path: z.string() },
101
+ handler: async (args) => fs.readFile(args.path, "utf-8"),
102
+ });
103
+
104
+ await server.start();
105
+ ```
106
+
107
+ ## Liens
108
+
109
+ - Docs : https://sentinel-api.wenjoseph16.workers.dev/docs
110
+ - Quickstart : https://sentinel-api.wenjoseph16.workers.dev/quickstart
111
+ - SDK Python : [`pip install starkgate-sdk`](https://pypi.org/project/starkgate-sdk/)
112
+ - Repo : https://github.com/wenjoseph16/starkgate
113
+
114
+ ## License
115
+
116
+ MIT
package/dist/cli.d.ts ADDED
@@ -0,0 +1,15 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * starkgate-mcp-server — CLI (npx starkgate-mcp-server)
4
+ *
5
+ * Serveur MCP stdio autonome qui expose StarkGate comme tools MCP :
6
+ * - starkgate_evaluate : évalue une action contre une policy (cloud, fail-closed)
7
+ * - starkgate_health : ping de l'API (aucun secret requis)
8
+ *
9
+ * Configuration par variables d'environnement :
10
+ * STARKGATE_API_KEY (requis pour evaluate) ex: sk_live_...
11
+ * STARKGATE_API_URL (optionnel) défaut: https://sentinel-api.wenjoseph16.workers.dev
12
+ * STARKGATE_AGENT_ID (optionnel) défaut: mcp-agent
13
+ * STARKGATE_POLICY_ID (optionnel) défaut: pol-demo
14
+ */
15
+ export {};
package/dist/cli.js ADDED
@@ -0,0 +1,131 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ /**
4
+ * starkgate-mcp-server — CLI (npx starkgate-mcp-server)
5
+ *
6
+ * Serveur MCP stdio autonome qui expose StarkGate comme tools MCP :
7
+ * - starkgate_evaluate : évalue une action contre une policy (cloud, fail-closed)
8
+ * - starkgate_health : ping de l'API (aucun secret requis)
9
+ *
10
+ * Configuration par variables d'environnement :
11
+ * STARKGATE_API_KEY (requis pour evaluate) ex: sk_live_...
12
+ * STARKGATE_API_URL (optionnel) défaut: https://sentinel-api.wenjoseph16.workers.dev
13
+ * STARKGATE_AGENT_ID (optionnel) défaut: mcp-agent
14
+ * STARKGATE_POLICY_ID (optionnel) défaut: pol-demo
15
+ */
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ const mcp_js_1 = require("@modelcontextprotocol/sdk/server/mcp.js");
18
+ const stdio_js_1 = require("@modelcontextprotocol/sdk/server/stdio.js");
19
+ const zod_1 = require("zod");
20
+ const DEFAULT_API_URL = "https://sentinel-api.wenjoseph16.workers.dev";
21
+ async function evaluateAction(apiUrl, apiKey, policyId, agentId, actionType, payload) {
22
+ const failClosed = (decision, reason) => ({
23
+ decision,
24
+ reason,
25
+ matchedRule: null,
26
+ auditHash: null,
27
+ riskScore: null,
28
+ });
29
+ // Sans clé : pas de round-trip inutile vers l'API — DENY immédiat,
30
+ // cohérent avec le warning affiché au démarrage.
31
+ if (!apiKey) {
32
+ return failClosed("DENY", "StarkGate API key is not set (STARKGATE_API_KEY) — request denied (fail-closed)");
33
+ }
34
+ try {
35
+ const response = await fetch(`${apiUrl}/v1/evaluate`, {
36
+ method: "POST",
37
+ headers: {
38
+ "Content-Type": "application/json",
39
+ Authorization: `Bearer ${apiKey}`,
40
+ },
41
+ body: JSON.stringify({
42
+ agentId,
43
+ policyId,
44
+ actionType,
45
+ payload,
46
+ nonce: crypto.randomUUID(),
47
+ timestamp: new Date().toISOString(),
48
+ }),
49
+ });
50
+ if (!response.ok) {
51
+ // Fail-closed : erreur d'évaluation => DENY (pas de fail-open silencieux).
52
+ return failClosed("DENY", `StarkGate evaluation failed (HTTP ${response.status})`);
53
+ }
54
+ const json = (await response.json());
55
+ const d = json?.data;
56
+ if (!d || typeof d.decision !== "string") {
57
+ return failClosed("DENY", "StarkGate returned an unexpected response");
58
+ }
59
+ return {
60
+ decision: d.decision === "DENY" ? "DENY" : "ALLOW",
61
+ reason: d.reason ?? "",
62
+ matchedRule: d.matchedRule ?? null,
63
+ auditHash: d.auditHash ?? null,
64
+ riskScore: typeof d.riskScore === "number" ? d.riskScore : null,
65
+ };
66
+ }
67
+ catch (error) {
68
+ return failClosed("DENY", `StarkGate evaluation error: ${error instanceof Error ? error.message : "unknown"}`);
69
+ }
70
+ }
71
+ async function healthCheck(apiUrl) {
72
+ try {
73
+ const response = await fetch(`${apiUrl}/health`);
74
+ if (!response.ok)
75
+ return { status: `http_${response.status}`, apiUrl };
76
+ const json = (await response.json());
77
+ return { status: json.status ?? "unknown", apiUrl };
78
+ }
79
+ catch (error) {
80
+ return {
81
+ status: `unreachable: ${error instanceof Error ? error.message : "unknown"}`,
82
+ apiUrl,
83
+ };
84
+ }
85
+ }
86
+ async function main() {
87
+ const apiUrl = (process.env.STARKGATE_API_URL || DEFAULT_API_URL).replace(/\/$/, "");
88
+ const apiKey = process.env.STARKGATE_API_KEY || "";
89
+ const agentId = process.env.STARKGATE_AGENT_ID || "mcp-agent";
90
+ const policyId = process.env.STARKGATE_POLICY_ID || "pol-demo";
91
+ const mcp = new mcp_js_1.McpServer({ name: "starkgate-mcp-server", version: "0.1.0" });
92
+ mcp.tool("starkgate_evaluate", "Evaluate an agent action against a StarkGate policy (cloud, fail-closed). Returns decision ALLOW/DENY, reason, matched rule and the offline-verifiable auditHash proof.", {
93
+ actionType: zod_1.z
94
+ .string()
95
+ .min(1)
96
+ .describe("Type of action, e.g. terminal.command, sql_query, send_email"),
97
+ payload: zod_1.z
98
+ .record(zod_1.z.string(), zod_1.z.unknown())
99
+ .describe("Action payload to evaluate, e.g. { \"cmd\": \"rm -rf /\" }"),
100
+ agentId: zod_1.z.string().min(1).optional().describe("Override agent id (default: STARKGATE_AGENT_ID env or 'mcp-agent')"),
101
+ policyId: zod_1.z.string().min(1).optional().describe("Override policy id (default: STARKGATE_POLICY_ID env or 'pol-demo')"),
102
+ }, async (args) => {
103
+ const out = await evaluateAction(apiUrl, apiKey, args.policyId || policyId, args.agentId || agentId, args.actionType, args.payload);
104
+ return {
105
+ content: [
106
+ {
107
+ type: "text",
108
+ text: JSON.stringify(out, null, 2),
109
+ },
110
+ ],
111
+ };
112
+ });
113
+ mcp.tool("starkgate_health", "Check StarkGate API reachability. No API key required.", {}, async () => {
114
+ const out = await healthCheck(apiUrl);
115
+ return {
116
+ content: [{ type: "text", text: JSON.stringify(out, null, 2) }],
117
+ };
118
+ });
119
+ if (!apiKey) {
120
+ // Pas de crash : starkgate_evaluate répondra DENY fail-closed avec un
121
+ // message explicite ; starkgate_health reste utilisable.
122
+ console.error("starkgate-mcp-server: STARKGATE_API_KEY is not set — starkgate_evaluate will DENY (fail-closed). Set it in your MCP client config.");
123
+ }
124
+ const transport = new stdio_js_1.StdioServerTransport();
125
+ await mcp.connect(transport);
126
+ console.error(`starkgate-mcp-server started (api: ${apiUrl})`);
127
+ }
128
+ main().catch((error) => {
129
+ console.error("starkgate-mcp-server fatal:", error);
130
+ process.exit(1);
131
+ });
@@ -0,0 +1,47 @@
1
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ export interface StarkGateConfig {
3
+ apiUrl: string;
4
+ apiKey: string;
5
+ policyId: string;
6
+ failClosed?: boolean;
7
+ }
8
+ export interface EvaluateResult {
9
+ decision: "ALLOW" | "DENY";
10
+ reason: string;
11
+ matchedRule: string | null;
12
+ wouldBlock: boolean;
13
+ }
14
+ export interface WrappedTool {
15
+ name: string;
16
+ description: string;
17
+ inputSchema: any;
18
+ handler: (args: any) => Promise<any>;
19
+ }
20
+ /**
21
+ * Create a StarkGate-guarded MCP server that intercepts all tool calls.
22
+ *
23
+ * Usage:
24
+ * ```typescript
25
+ * const server = createStarkGateMcpServer({
26
+ * apiUrl: "https://starkgate.example.com",
27
+ * apiKey: "sk-...",
28
+ * policyId: "pol_abc123",
29
+ * });
30
+ *
31
+ * // Wrap any tool
32
+ * server.wrapTool({
33
+ * name: "read_file",
34
+ * description: "Read a file from the filesystem",
35
+ * inputSchema: { path: z.string() },
36
+ * handler: async (args) => fs.readFile(args.path, "utf-8"),
37
+ * });
38
+ *
39
+ * server.start();
40
+ * ```
41
+ */
42
+ export declare function createStarkGateMcpServer(config: StarkGateConfig): {
43
+ wrapTool: (tool: WrappedTool) => void;
44
+ start: () => Promise<void>;
45
+ evaluateAction: (agentId: string, actionType: string, payload: Record<string, any>) => Promise<EvaluateResult>;
46
+ mcp: McpServer;
47
+ };
package/dist/index.js ADDED
@@ -0,0 +1,161 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createStarkGateMcpServer = createStarkGateMcpServer;
4
+ const mcp_js_1 = require("@modelcontextprotocol/sdk/server/mcp.js");
5
+ const stdio_js_1 = require("@modelcontextprotocol/sdk/server/stdio.js");
6
+ /**
7
+ * Create a StarkGate-guarded MCP server that intercepts all tool calls.
8
+ *
9
+ * Usage:
10
+ * ```typescript
11
+ * const server = createStarkGateMcpServer({
12
+ * apiUrl: "https://starkgate.example.com",
13
+ * apiKey: "sk-...",
14
+ * policyId: "pol_abc123",
15
+ * });
16
+ *
17
+ * // Wrap any tool
18
+ * server.wrapTool({
19
+ * name: "read_file",
20
+ * description: "Read a file from the filesystem",
21
+ * inputSchema: { path: z.string() },
22
+ * handler: async (args) => fs.readFile(args.path, "utf-8"),
23
+ * });
24
+ *
25
+ * server.start();
26
+ * ```
27
+ */
28
+ function createStarkGateMcpServer(config) {
29
+ const mcp = new mcp_js_1.McpServer({
30
+ name: "starkgate-mcp-server",
31
+ version: "0.1.0",
32
+ });
33
+ const wrappedTools = new Map();
34
+ /**
35
+ * Evaluate an action against StarkGate before executing it.
36
+ * Returns true if ALLOW, false if DENY.
37
+ */
38
+ async function evaluateAction(agentId, actionType, payload) {
39
+ try {
40
+ const nonce = crypto.randomUUID();
41
+ const timestamp = new Date().toISOString();
42
+ const response = await fetch(`${config.apiUrl}/v1/evaluate`, {
43
+ method: "POST",
44
+ headers: {
45
+ "Content-Type": "application/json",
46
+ Authorization: `Bearer ${config.apiKey}`,
47
+ },
48
+ body: JSON.stringify({
49
+ agentId,
50
+ policyId: config.policyId,
51
+ actionType,
52
+ payload,
53
+ nonce,
54
+ timestamp,
55
+ }),
56
+ });
57
+ if (!response.ok) {
58
+ // Fail-closed: if evaluation fails, deny by default
59
+ if (config.failClosed !== false) {
60
+ return {
61
+ decision: "DENY",
62
+ reason: `StarkGate evaluation failed (HTTP ${response.status})`,
63
+ matchedRule: null,
64
+ wouldBlock: false,
65
+ };
66
+ }
67
+ // Fail-open: allow if explicitly configured
68
+ return {
69
+ decision: "ALLOW",
70
+ reason: "StarkGate unavailable, fail-open mode",
71
+ matchedRule: null,
72
+ wouldBlock: false,
73
+ };
74
+ }
75
+ const result = await response.json();
76
+ return {
77
+ decision: result.data.decision,
78
+ reason: result.data.reason,
79
+ matchedRule: result.data.matchedRule,
80
+ wouldBlock: result.data.wouldBlock,
81
+ };
82
+ }
83
+ catch (error) {
84
+ // Fail-closed on network error
85
+ if (config.failClosed !== false) {
86
+ return {
87
+ decision: "DENY",
88
+ reason: `StarkGate evaluation error: ${error instanceof Error ? error.message : "unknown"}`,
89
+ matchedRule: null,
90
+ wouldBlock: false,
91
+ };
92
+ }
93
+ return {
94
+ decision: "ALLOW",
95
+ reason: "StarkGate unavailable, fail-open mode",
96
+ matchedRule: null,
97
+ wouldBlock: false,
98
+ };
99
+ }
100
+ }
101
+ /**
102
+ * Wrap a tool with StarkGate guard.
103
+ * The tool will be evaluated before execution.
104
+ */
105
+ function wrapTool(tool) {
106
+ wrappedTools.set(tool.name, tool);
107
+ mcp.tool(tool.name, tool.description, tool.inputSchema, async (args) => {
108
+ // Evaluate against StarkGate before execution
109
+ const agentId = "mcp-agent"; // Could be configurable
110
+ const evaluation = await evaluateAction(agentId, tool.name, args);
111
+ if (evaluation.decision === "DENY") {
112
+ return {
113
+ content: [
114
+ {
115
+ type: "text",
116
+ text: `đźš« BLOCKED by StarkGate: ${evaluation.reason}${evaluation.matchedRule ? ` (rule: ${evaluation.matchedRule})` : ""}`,
117
+ },
118
+ ],
119
+ isError: true,
120
+ };
121
+ }
122
+ // Execute the original tool
123
+ try {
124
+ const result = await tool.handler(args);
125
+ return {
126
+ content: [
127
+ {
128
+ type: "text",
129
+ text: typeof result === "string" ? result : JSON.stringify(result, null, 2),
130
+ },
131
+ ],
132
+ };
133
+ }
134
+ catch (error) {
135
+ return {
136
+ content: [
137
+ {
138
+ type: "text",
139
+ text: `Error executing ${tool.name}: ${error instanceof Error ? error.message : "unknown error"}`,
140
+ },
141
+ ],
142
+ isError: true,
143
+ };
144
+ }
145
+ });
146
+ }
147
+ /**
148
+ * Start the MCP server on stdio transport.
149
+ */
150
+ async function start() {
151
+ const transport = new stdio_js_1.StdioServerTransport();
152
+ await mcp.connect(transport);
153
+ console.error("StarkGate MCP Server started");
154
+ }
155
+ return {
156
+ wrapTool,
157
+ start,
158
+ evaluateAction, // Exposed for testing
159
+ mcp, // Exposed for testing
160
+ };
161
+ }
package/package.json ADDED
@@ -0,0 +1,52 @@
1
+ {
2
+ "name": "starkgate-mcp-server",
3
+ "version": "0.1.0",
4
+ "description": "MCP server that routes agent tool calls through StarkGate's universal firewall — fail-closed evaluation with offline-verifiable proofs.",
5
+ "license": "MIT",
6
+ "bin": {
7
+ "starkgate-mcp-server": "dist/cli.js"
8
+ },
9
+ "main": "dist/index.js",
10
+ "types": "dist/index.d.ts",
11
+ "files": [
12
+ "dist",
13
+ "README.md"
14
+ ],
15
+ "repository": {
16
+ "type": "git",
17
+ "url": "git+https://github.com/wenjoseph16/starkgate.git",
18
+ "directory": "starkgate-mcp-server"
19
+ },
20
+ "homepage": "https://sentinel-api.wenjoseph16.workers.dev/docs",
21
+ "bugs": "https://github.com/wenjoseph16/starkgate/issues",
22
+ "keywords": [
23
+ "mcp",
24
+ "model-context-protocol",
25
+ "ai-agent",
26
+ "firewall",
27
+ "security",
28
+ "guard",
29
+ "policy",
30
+ "starkgate",
31
+ "claude",
32
+ "tool-calling"
33
+ ],
34
+ "engines": {
35
+ "node": ">=18"
36
+ },
37
+ "scripts": {
38
+ "build": "tsc",
39
+ "test": "vitest run",
40
+ "dev": "tsc --watch",
41
+ "prepublishOnly": "npm run build && npm test"
42
+ },
43
+ "dependencies": {
44
+ "@modelcontextprotocol/sdk": "^1.0.0",
45
+ "zod": "^4.5.2"
46
+ },
47
+ "devDependencies": {
48
+ "@types/node": "^20.0.0",
49
+ "typescript": "^5.3.0",
50
+ "vitest": "^1.0.0"
51
+ }
52
+ }