equisense-research-mcp 0.1.1 → 0.2.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/index.js +38 -13
- package/package.json +2 -2
package/index.js
CHANGED
|
@@ -1,18 +1,24 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
// EquiSense Research MCP server.
|
|
3
3
|
//
|
|
4
|
-
// Single tool: ask_research — proxies POST /api/v1/research/ask. The
|
|
5
|
-
//
|
|
6
|
-
//
|
|
7
|
-
// in here through the EQUISENSE_MCP_TOKEN env var.
|
|
4
|
+
// Single tool: ask_research — proxies POST /api/v1/research/ask. The user mints a
|
|
5
|
+
// scoped, revocable token in the web app (Settings → Claude) and passes it here
|
|
6
|
+
// via the EQUISENSE_MCP_TOKEN env var.
|
|
8
7
|
//
|
|
9
|
-
//
|
|
10
|
-
//
|
|
8
|
+
// Auth: we send the token BOTH as `Authorization: Bearer <token>` and as a
|
|
9
|
+
// `Cookie: ES_AUTH=<token>` header. New self-serve tokens are RESEARCH-scoped and
|
|
10
|
+
// authenticate via the Bearer path (McpAuthFilter); legacy admin-minted full-session
|
|
11
|
+
// tokens authenticate via the cookie path (PersistentAuthFilter). Sending both means
|
|
12
|
+
// either kind of token works with no client-side branching — the backend ignores the
|
|
13
|
+
// header that doesn't apply.
|
|
14
|
+
//
|
|
15
|
+
// Treat the token like a password. A scoped token grants read-only research access;
|
|
16
|
+
// revoke it any time in Settings → Claude. Never log it. Never commit it.
|
|
11
17
|
//
|
|
12
18
|
// Transport: stdio. Register in Claude Code via:
|
|
13
19
|
// claude mcp add equisense-research --scope local \
|
|
14
|
-
// --env EQUISENSE_BASE_URL=
|
|
15
|
-
// --env EQUISENSE_MCP_TOKEN='<
|
|
20
|
+
// --env EQUISENSE_BASE_URL=https://equisense.ai \
|
|
21
|
+
// --env EQUISENSE_MCP_TOKEN='<token from Settings → Claude>' \
|
|
16
22
|
// -- node /abs/path/to/index.js
|
|
17
23
|
|
|
18
24
|
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
@@ -31,12 +37,18 @@ const MCP_TOKEN = process.env.EQUISENSE_MCP_TOKEN;
|
|
|
31
37
|
|
|
32
38
|
if (!MCP_TOKEN || MCP_TOKEN.trim() === "") {
|
|
33
39
|
console.error(
|
|
34
|
-
"FATAL: EQUISENSE_MCP_TOKEN env var is required.
|
|
35
|
-
"
|
|
40
|
+
"FATAL: EQUISENSE_MCP_TOKEN env var is required. Generate one in the " +
|
|
41
|
+
"EquiSense web app under Settings → Claude.",
|
|
36
42
|
);
|
|
37
43
|
process.exit(1);
|
|
38
44
|
}
|
|
39
45
|
|
|
46
|
+
// Client identity from the MCP initialize handshake (e.g. "claude-ai",
|
|
47
|
+
// "Claude Code"). Captured once, forwarded as X-MCP-Client so the backend can
|
|
48
|
+
// auto-name the connection in Settings → Claude. Null until the handshake lands
|
|
49
|
+
// and when askResearch is exercised directly in unit tests.
|
|
50
|
+
let clientName = null;
|
|
51
|
+
|
|
40
52
|
export const TOOLS = [
|
|
41
53
|
{
|
|
42
54
|
name: "ask_research",
|
|
@@ -83,7 +95,10 @@ async function callRest(path, options = {}) {
|
|
|
83
95
|
headers: {
|
|
84
96
|
"Content-Type": "application/json",
|
|
85
97
|
Accept: "application/json",
|
|
98
|
+
// Bearer for new scoped tokens; Cookie for legacy session tokens. See header note.
|
|
99
|
+
Authorization: `Bearer ${MCP_TOKEN}`,
|
|
86
100
|
Cookie: `ES_AUTH=${MCP_TOKEN}`,
|
|
101
|
+
...(clientName ? { "X-MCP-Client": clientName } : {}),
|
|
87
102
|
...(options.headers || {}),
|
|
88
103
|
},
|
|
89
104
|
});
|
|
@@ -103,8 +118,8 @@ function formatHttpError(path, status, bodyText) {
|
|
|
103
118
|
return (
|
|
104
119
|
"Auth failed (HTTP 401) calling " +
|
|
105
120
|
path +
|
|
106
|
-
". EQUISENSE_MCP_TOKEN is expired or invalid.
|
|
107
|
-
"
|
|
121
|
+
". EQUISENSE_MCP_TOKEN is expired, revoked, or invalid. Generate a fresh one " +
|
|
122
|
+
"in the EquiSense web app under Settings → Claude and update the env var."
|
|
108
123
|
);
|
|
109
124
|
}
|
|
110
125
|
if (status === 402) {
|
|
@@ -163,7 +178,7 @@ function createServer() {
|
|
|
163
178
|
const server = new Server(
|
|
164
179
|
{
|
|
165
180
|
name: "equisense-research",
|
|
166
|
-
version: "0.
|
|
181
|
+
version: "0.2.0",
|
|
167
182
|
},
|
|
168
183
|
{
|
|
169
184
|
capabilities: {
|
|
@@ -172,6 +187,16 @@ function createServer() {
|
|
|
172
187
|
},
|
|
173
188
|
);
|
|
174
189
|
|
|
190
|
+
// Capture the client identity once the initialize handshake completes, so
|
|
191
|
+
// callRest can forward it as X-MCP-Client for auto-naming the connection.
|
|
192
|
+
server.oninitialized = () => {
|
|
193
|
+
try {
|
|
194
|
+
clientName = server.getClientVersion()?.name || null;
|
|
195
|
+
} catch {
|
|
196
|
+
clientName = null;
|
|
197
|
+
}
|
|
198
|
+
};
|
|
199
|
+
|
|
175
200
|
server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
176
201
|
tools: TOOLS,
|
|
177
202
|
}));
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "equisense-research-mcp",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "MCP server wrapper for the EquiSense AI equity-research API. Exposes a single ask_research tool that proxies POST /api/v1/research/ask, authenticated via a minted
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "MCP server wrapper for the EquiSense AI equity-research API. Exposes a single ask_research tool that proxies POST /api/v1/research/ask, authenticated via a scoped, revocable token minted in Settings → Claude.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "index.js",
|
|
7
7
|
"bin": {
|