perceptia-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/dist/cli.d.ts +2 -0
- package/dist/cli.js +38 -0
- package/dist/server.d.ts +1 -0
- package/dist/server.js +99 -0
- package/dist/setup.d.ts +1 -0
- package/dist/setup.js +105 -0
- package/package.json +35 -0
package/dist/cli.d.ts
ADDED
package/dist/cli.js
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { startServer } from "./server.js";
|
|
3
|
+
import { runSetup } from "./setup.js";
|
|
4
|
+
const args = process.argv.slice(2);
|
|
5
|
+
const command = args[0];
|
|
6
|
+
if (command === "setup") {
|
|
7
|
+
const apiKey = args[1];
|
|
8
|
+
if (!apiKey) {
|
|
9
|
+
console.log("Perceptia MCP — Setup");
|
|
10
|
+
console.log("");
|
|
11
|
+
console.log("Usage: npx perceptia-mcp setup <API_KEY>");
|
|
12
|
+
console.log("");
|
|
13
|
+
console.log(" API_KEY Your Perceptia API key (starts with ck_live_)");
|
|
14
|
+
console.log(" Get one at https://perceptiamcp.com/dashboard");
|
|
15
|
+
process.exit(1);
|
|
16
|
+
}
|
|
17
|
+
await runSetup(apiKey);
|
|
18
|
+
}
|
|
19
|
+
else if (command === "help" || command === "--help" || command === "-h") {
|
|
20
|
+
console.log("Perceptia MCP Server");
|
|
21
|
+
console.log("");
|
|
22
|
+
console.log("Commands:");
|
|
23
|
+
console.log(" setup <API_KEY> Configure Perceptia for your AI clients");
|
|
24
|
+
console.log(" (no command) Run the MCP server (used by AI clients)");
|
|
25
|
+
console.log("");
|
|
26
|
+
console.log("Setup: npx perceptia-mcp setup ck_live_xxx");
|
|
27
|
+
console.log("Docs: https://perceptiamcp.com");
|
|
28
|
+
}
|
|
29
|
+
else {
|
|
30
|
+
// Default: run MCP server via stdio
|
|
31
|
+
const apiKey = process.env.PERCEPTIA_API_KEY;
|
|
32
|
+
if (!apiKey) {
|
|
33
|
+
console.error("Error: PERCEPTIA_API_KEY environment variable is required.");
|
|
34
|
+
console.error("Run 'npx perceptia-mcp setup <KEY>' to configure automatically.");
|
|
35
|
+
process.exit(1);
|
|
36
|
+
}
|
|
37
|
+
await startServer(apiKey);
|
|
38
|
+
}
|
package/dist/server.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function startServer(apiKey: string): Promise<void>;
|
package/dist/server.js
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
2
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
3
|
+
import { CallToolRequestSchema, ListToolsRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
|
|
4
|
+
const API_BASE = "https://api.perceptiamcp.com";
|
|
5
|
+
export async function startServer(apiKey) {
|
|
6
|
+
const server = new Server({ name: "perceptia-mcp", version: "1.0.0" }, { capabilities: { tools: {} } });
|
|
7
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
8
|
+
tools: [
|
|
9
|
+
{
|
|
10
|
+
name: "ground",
|
|
11
|
+
description: "Get grounding context from Perceptia for a coding query. Returns best-practice guidance relevant to the query.",
|
|
12
|
+
inputSchema: {
|
|
13
|
+
type: "object",
|
|
14
|
+
properties: {
|
|
15
|
+
query: {
|
|
16
|
+
type: "string",
|
|
17
|
+
description: "The coding topic or question to get grounding for",
|
|
18
|
+
},
|
|
19
|
+
},
|
|
20
|
+
required: ["query"],
|
|
21
|
+
},
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
name: "status",
|
|
25
|
+
description: "Check your Perceptia API usage and device status",
|
|
26
|
+
inputSchema: {
|
|
27
|
+
type: "object",
|
|
28
|
+
properties: {},
|
|
29
|
+
},
|
|
30
|
+
},
|
|
31
|
+
],
|
|
32
|
+
}));
|
|
33
|
+
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
34
|
+
const { name, arguments: args } = request.params;
|
|
35
|
+
if (name === "ground") {
|
|
36
|
+
const query = args?.query;
|
|
37
|
+
if (!query || typeof query !== "string") {
|
|
38
|
+
return {
|
|
39
|
+
content: [{ type: "text", text: "Error: query parameter is required" }],
|
|
40
|
+
isError: true,
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
const fingerprint = `mcp-client-${apiKey.slice(-8)}`;
|
|
44
|
+
const res = await fetch(`${API_BASE}/v1/ground`, {
|
|
45
|
+
method: "POST",
|
|
46
|
+
headers: {
|
|
47
|
+
"Content-Type": "application/json",
|
|
48
|
+
Authorization: `Bearer ${apiKey}`,
|
|
49
|
+
"X-Fingerprint": fingerprint,
|
|
50
|
+
},
|
|
51
|
+
body: JSON.stringify({ query }),
|
|
52
|
+
});
|
|
53
|
+
if (res.status === 401) {
|
|
54
|
+
return {
|
|
55
|
+
content: [{ type: "text", text: "Error: Invalid API key. Check your PERCEPTIA_API_KEY." }],
|
|
56
|
+
isError: true,
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
if (res.status === 429) {
|
|
60
|
+
return {
|
|
61
|
+
content: [{ type: "text", text: "Error: Monthly quota exceeded. Upgrade your plan at perceptiamcp.com" }],
|
|
62
|
+
isError: true,
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
if (!res.ok) {
|
|
66
|
+
const body = await res.text();
|
|
67
|
+
return {
|
|
68
|
+
content: [{ type: "text", text: `Error (${res.status}): ${body}` }],
|
|
69
|
+
isError: true,
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
const groundingText = await res.text();
|
|
73
|
+
return {
|
|
74
|
+
content: [{ type: "text", text: groundingText }],
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
if (name === "status") {
|
|
78
|
+
const res = await fetch(`${API_BASE}/v1/status`, {
|
|
79
|
+
headers: { Authorization: `Bearer ${apiKey}` },
|
|
80
|
+
});
|
|
81
|
+
if (!res.ok) {
|
|
82
|
+
return {
|
|
83
|
+
content: [{ type: "text", text: `Error (${res.status}): ${await res.text()}` }],
|
|
84
|
+
isError: true,
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
const data = await res.json();
|
|
88
|
+
return {
|
|
89
|
+
content: [{ type: "text", text: JSON.stringify(data, null, 2) }],
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
return {
|
|
93
|
+
content: [{ type: "text", text: `Unknown tool: ${name}` }],
|
|
94
|
+
isError: true,
|
|
95
|
+
};
|
|
96
|
+
});
|
|
97
|
+
const transport = new StdioServerTransport();
|
|
98
|
+
await server.connect(transport);
|
|
99
|
+
}
|
package/dist/setup.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function runSetup(apiKey: string): Promise<void>;
|
package/dist/setup.js
ADDED
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import os from "node:os";
|
|
4
|
+
function getClients() {
|
|
5
|
+
const home = os.homedir();
|
|
6
|
+
const isWin = process.platform === "win32";
|
|
7
|
+
const isMac = process.platform === "darwin";
|
|
8
|
+
const appData = process.env.APPDATA || "";
|
|
9
|
+
const clients = [];
|
|
10
|
+
// Claude Desktop
|
|
11
|
+
const claudeDesktopPath = isWin
|
|
12
|
+
? path.join(appData, "Claude", "claude_desktop_config.json")
|
|
13
|
+
: isMac
|
|
14
|
+
? path.join(home, "Library", "Application Support", "Claude", "claude_desktop_config.json")
|
|
15
|
+
: path.join(home, ".config", "Claude", "claude_desktop_config.json");
|
|
16
|
+
if (fs.existsSync(path.dirname(claudeDesktopPath))) {
|
|
17
|
+
clients.push({ name: "Claude Desktop", configPath: claudeDesktopPath });
|
|
18
|
+
}
|
|
19
|
+
// Claude Code
|
|
20
|
+
const claudeCodePath = path.join(home, ".claude", "settings.json");
|
|
21
|
+
if (fs.existsSync(path.dirname(claudeCodePath))) {
|
|
22
|
+
clients.push({ name: "Claude Code", configPath: claudeCodePath });
|
|
23
|
+
}
|
|
24
|
+
// Cursor
|
|
25
|
+
const cursorPath = isWin
|
|
26
|
+
? path.join(appData, "Cursor", "User", "globalStorage", "cursor.mcp", "mcp.json")
|
|
27
|
+
: isMac
|
|
28
|
+
? path.join(home, "Library", "Application Support", "Cursor", "User", "globalStorage", "cursor.mcp", "mcp.json")
|
|
29
|
+
: path.join(home, ".config", "Cursor", "User", "globalStorage", "cursor.mcp", "mcp.json");
|
|
30
|
+
if (fs.existsSync(path.dirname(cursorPath))) {
|
|
31
|
+
clients.push({ name: "Cursor", configPath: cursorPath });
|
|
32
|
+
}
|
|
33
|
+
return clients;
|
|
34
|
+
}
|
|
35
|
+
function writeConfig(configPath, apiKey) {
|
|
36
|
+
const dir = path.dirname(configPath);
|
|
37
|
+
if (!fs.existsSync(dir)) {
|
|
38
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
39
|
+
}
|
|
40
|
+
let config = {};
|
|
41
|
+
if (fs.existsSync(configPath)) {
|
|
42
|
+
try {
|
|
43
|
+
config = JSON.parse(fs.readFileSync(configPath, "utf-8"));
|
|
44
|
+
}
|
|
45
|
+
catch {
|
|
46
|
+
config = {};
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
if (!config.mcpServers || typeof config.mcpServers !== "object") {
|
|
50
|
+
config.mcpServers = {};
|
|
51
|
+
}
|
|
52
|
+
config.mcpServers.perceptia = {
|
|
53
|
+
command: "npx",
|
|
54
|
+
args: ["-y", "perceptia-mcp"],
|
|
55
|
+
env: {
|
|
56
|
+
PERCEPTIA_API_KEY: apiKey,
|
|
57
|
+
},
|
|
58
|
+
};
|
|
59
|
+
fs.writeFileSync(configPath, JSON.stringify(config, null, 2), "utf-8");
|
|
60
|
+
}
|
|
61
|
+
export async function runSetup(apiKey) {
|
|
62
|
+
if (!apiKey.startsWith("ck_live_")) {
|
|
63
|
+
console.error("Error: Invalid API key format. Keys start with ck_live_");
|
|
64
|
+
console.error("Get your key at https://perceptiamcp.com/dashboard");
|
|
65
|
+
process.exit(1);
|
|
66
|
+
}
|
|
67
|
+
// Validate key against API
|
|
68
|
+
console.log("Validating API key...");
|
|
69
|
+
try {
|
|
70
|
+
const res = await fetch("https://api.perceptiamcp.com/v1/status", {
|
|
71
|
+
headers: { Authorization: `Bearer ${apiKey}` },
|
|
72
|
+
});
|
|
73
|
+
if (res.status === 401) {
|
|
74
|
+
console.error("Error: API key is invalid or expired.");
|
|
75
|
+
console.error("Check your keys at https://perceptiamcp.com/dashboard");
|
|
76
|
+
process.exit(1);
|
|
77
|
+
}
|
|
78
|
+
if (!res.ok) {
|
|
79
|
+
console.error(`Warning: Could not validate key (${res.status}). Continuing anyway.`);
|
|
80
|
+
}
|
|
81
|
+
else {
|
|
82
|
+
console.log("API key is valid.");
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
catch {
|
|
86
|
+
console.error("Warning: Could not reach API to validate key. Continuing anyway.");
|
|
87
|
+
}
|
|
88
|
+
const clients = getClients();
|
|
89
|
+
if (clients.length === 0) {
|
|
90
|
+
console.error("No supported AI clients found (Claude Desktop, Claude Code, Cursor).");
|
|
91
|
+
console.error("Install one of them first, then run this command again.");
|
|
92
|
+
process.exit(1);
|
|
93
|
+
}
|
|
94
|
+
console.log("");
|
|
95
|
+
console.log(`Found ${clients.length} client(s):`);
|
|
96
|
+
for (const client of clients) {
|
|
97
|
+
writeConfig(client.configPath, apiKey);
|
|
98
|
+
console.log(` + ${client.name} — configured at ${client.configPath}`);
|
|
99
|
+
}
|
|
100
|
+
console.log("");
|
|
101
|
+
console.log("Done! Restart your AI client to activate Perceptia grounding.");
|
|
102
|
+
console.log("");
|
|
103
|
+
console.log("Usage: Ask your AI assistant anything — it now has access to the 'ground' tool.");
|
|
104
|
+
console.log(" It will automatically fetch relevant grounding context from Perceptia.");
|
|
105
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "perceptia-mcp",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "MCP server for Perceptia grounding API — one command setup for Claude, Cursor, and more",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"perceptia-mcp": "dist/cli.js"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"dist"
|
|
11
|
+
],
|
|
12
|
+
"scripts": {
|
|
13
|
+
"build": "tsc",
|
|
14
|
+
"prepublishOnly": "npm run build"
|
|
15
|
+
},
|
|
16
|
+
"keywords": [
|
|
17
|
+
"mcp",
|
|
18
|
+
"perceptia",
|
|
19
|
+
"grounding",
|
|
20
|
+
"claude",
|
|
21
|
+
"ai",
|
|
22
|
+
"context"
|
|
23
|
+
],
|
|
24
|
+
"author": "Perceptia",
|
|
25
|
+
"license": "MIT",
|
|
26
|
+
"dependencies": {
|
|
27
|
+
"@modelcontextprotocol/sdk": "^1.10.0"
|
|
28
|
+
},
|
|
29
|
+
"devDependencies": {
|
|
30
|
+
"typescript": "^5.5.0"
|
|
31
|
+
},
|
|
32
|
+
"engines": {
|
|
33
|
+
"node": ">=18"
|
|
34
|
+
}
|
|
35
|
+
}
|