assay-mcp-server 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/LICENSE +22 -0
- package/README.md +421 -0
- package/dist/firebase.d.ts +53 -0
- package/dist/firebase.d.ts.map +1 -0
- package/dist/firebase.js +123 -0
- package/dist/firebase.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +380 -0
- package/dist/index.js.map +1 -0
- package/dist/tools/askQuestion.d.ts +9 -0
- package/dist/tools/askQuestion.d.ts.map +1 -0
- package/dist/tools/askQuestion.js +27 -0
- package/dist/tools/askQuestion.js.map +1 -0
- package/dist/tools/browseAllDocuments.d.ts +17 -0
- package/dist/tools/browseAllDocuments.d.ts.map +1 -0
- package/dist/tools/browseAllDocuments.js +44 -0
- package/dist/tools/browseAllDocuments.js.map +1 -0
- package/dist/tools/browseThemes.d.ts +19 -0
- package/dist/tools/browseThemes.d.ts.map +1 -0
- package/dist/tools/browseThemes.js +20 -0
- package/dist/tools/browseThemes.js.map +1 -0
- package/dist/tools/compareDocuments.d.ts +9 -0
- package/dist/tools/compareDocuments.d.ts.map +1 -0
- package/dist/tools/compareDocuments.js +52 -0
- package/dist/tools/compareDocuments.js.map +1 -0
- package/dist/tools/getDocumentSummary.d.ts +12 -0
- package/dist/tools/getDocumentSummary.d.ts.map +1 -0
- package/dist/tools/getDocumentSummary.js +29 -0
- package/dist/tools/getDocumentSummary.js.map +1 -0
- package/dist/tools/getLibraryInsight.d.ts +7 -0
- package/dist/tools/getLibraryInsight.d.ts.map +1 -0
- package/dist/tools/getLibraryInsight.js +14 -0
- package/dist/tools/getLibraryInsight.js.map +1 -0
- package/dist/tools/getSimilarDocuments.d.ts +25 -0
- package/dist/tools/getSimilarDocuments.d.ts.map +1 -0
- package/dist/tools/getSimilarDocuments.js +28 -0
- package/dist/tools/getSimilarDocuments.js.map +1 -0
- package/dist/tools/produceFaq.d.ts +50 -0
- package/dist/tools/produceFaq.d.ts.map +1 -0
- package/dist/tools/produceFaq.js +52 -0
- package/dist/tools/produceFaq.js.map +1 -0
- package/dist/tools/scoringUtils.d.ts +23 -0
- package/dist/tools/scoringUtils.d.ts.map +1 -0
- package/dist/tools/scoringUtils.js +61 -0
- package/dist/tools/scoringUtils.js.map +1 -0
- package/dist/tools/searchByAuthor.d.ts +15 -0
- package/dist/tools/searchByAuthor.d.ts.map +1 -0
- package/dist/tools/searchByAuthor.js +27 -0
- package/dist/tools/searchByAuthor.js.map +1 -0
- package/dist/tools/searchByKeywords.d.ts +16 -0
- package/dist/tools/searchByKeywords.d.ts.map +1 -0
- package/dist/tools/searchByKeywords.js +27 -0
- package/dist/tools/searchByKeywords.js.map +1 -0
- package/dist/tools/searchByTheme.d.ts +27 -0
- package/dist/tools/searchByTheme.d.ts.map +1 -0
- package/dist/tools/searchByTheme.js +27 -0
- package/dist/tools/searchByTheme.js.map +1 -0
- package/dist/tools/searchByTitle.d.ts +15 -0
- package/dist/tools/searchByTitle.d.ts.map +1 -0
- package/dist/tools/searchByTitle.js +27 -0
- package/dist/tools/searchByTitle.js.map +1 -0
- package/dist/tools/searchDocuments.d.ts +14 -0
- package/dist/tools/searchDocuments.d.ts.map +1 -0
- package/dist/tools/searchDocuments.js +28 -0
- package/dist/tools/searchDocuments.js.map +1 -0
- package/package.json +55 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Cirrusly Clever
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
22
|
+
|
package/README.md
ADDED
|
@@ -0,0 +1,421 @@
|
|
|
1
|
+
# Assay MCP Server
|
|
2
|
+
|
|
3
|
+
Connect Claude Desktop to your [Assay](https://assay.cirrusly-clever.com) document library. Search, explore, and extract knowledge from your research documents directly from Claude conversations.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
The Assay MCP Server provides Claude Desktop with access to your Assay document library through the Model Context Protocol (MCP). It enables Claude to:
|
|
8
|
+
|
|
9
|
+
- **Search** your library by themes, keywords, authors, and titles
|
|
10
|
+
- **Retrieve** document summaries (comprehensive, casual, and FAQs)
|
|
11
|
+
- **Discover** related documents using Jaccard similarity
|
|
12
|
+
- **Explore** themes and generate insights
|
|
13
|
+
- **Compare** documents and synthesize FAQs across multiple sources
|
|
14
|
+
|
|
15
|
+
All tools search both your **private** and **public** document collections, giving Claude access to your entire research library.
|
|
16
|
+
|
|
17
|
+
## Features
|
|
18
|
+
|
|
19
|
+
- 🔍 **13 powerful tools** for document exploration and analysis
|
|
20
|
+
- 🎯 **Jaccard similarity scoring** for intelligent document matching
|
|
21
|
+
- 🔐 **Secure authentication** via Firebase ID tokens
|
|
22
|
+
- 📊 **Theme-based organization** with 180+ canonical themes
|
|
23
|
+
- 🚀 **Zero GenAI costs** - tools only retrieve data, Claude does the synthesis (or optional server-side synthesis with your API key)
|
|
24
|
+
- 🌐 **Public + Private** - searches across both collections
|
|
25
|
+
|
|
26
|
+
## Installation
|
|
27
|
+
|
|
28
|
+
### Prerequisites
|
|
29
|
+
|
|
30
|
+
- Node.js 18+
|
|
31
|
+
- An Assay account ([sign up here](https://assay.cirrusly-clever.com))
|
|
32
|
+
- Claude Desktop installed
|
|
33
|
+
|
|
34
|
+
### Install from Source
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
git clone https://github.com/cirruslycurious/assay-mcp-server.git
|
|
38
|
+
cd assay-mcp-server
|
|
39
|
+
npm install
|
|
40
|
+
npm run build
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
### Global Installation (After Publishing)
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
npm install -g @assay/mcp-server
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## Quick Start
|
|
50
|
+
|
|
51
|
+
**New to Assay MCP?** See the [Complete Setup Guide](./SETUP_GUIDE.md) for detailed instructions on:
|
|
52
|
+
- Getting your Firebase token
|
|
53
|
+
- Optional: Setting up server-side synthesis with Claude API key
|
|
54
|
+
|
|
55
|
+
## Getting Your Firebase Token
|
|
56
|
+
|
|
57
|
+
1. Visit [Assay Dashboard](https://assay.cirrusly-clever.com/dashboard)
|
|
58
|
+
2. Sign in with Google (if not already signed in)
|
|
59
|
+
3. Click the **"Get MCP Token"** button in the header
|
|
60
|
+
4. Copy the token from the dialog
|
|
61
|
+
|
|
62
|
+
**Note:** Tokens expire after 1 hour. Generate a new token when needed.
|
|
63
|
+
|
|
64
|
+
## Claude Desktop Configuration
|
|
65
|
+
|
|
66
|
+
Add the MCP server to your Claude Desktop configuration:
|
|
67
|
+
|
|
68
|
+
**macOS:** `~/Library/Application Support/Claude/claude_desktop_config.json`
|
|
69
|
+
**Windows:** `%APPDATA%\Claude\claude_desktop_config.json`
|
|
70
|
+
**Linux:** `~/.config/Claude/claude_desktop_config.json`
|
|
71
|
+
|
|
72
|
+
### For Local Installation
|
|
73
|
+
|
|
74
|
+
```json
|
|
75
|
+
{
|
|
76
|
+
"mcpServers": {
|
|
77
|
+
"assay": {
|
|
78
|
+
"command": "node",
|
|
79
|
+
"args": [
|
|
80
|
+
"/absolute/path/to/assay-mcp-server/dist/index.js",
|
|
81
|
+
"--token",
|
|
82
|
+
"YOUR_FIREBASE_TOKEN_HERE"
|
|
83
|
+
]
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
**Replace `/absolute/path/to/assay-mcp-server`** with your actual path, e.g.:
|
|
90
|
+
- macOS: `/Users/yourname/Documents/assay-mcp-server/dist/index.js`
|
|
91
|
+
- Linux: `/home/yourname/assay-mcp-server/dist/index.js`
|
|
92
|
+
- Windows: `C:\\Users\\yourname\\Documents\\assay-mcp-server\\dist\\index.js`
|
|
93
|
+
|
|
94
|
+
### For Global Installation
|
|
95
|
+
|
|
96
|
+
```json
|
|
97
|
+
{
|
|
98
|
+
"mcpServers": {
|
|
99
|
+
"assay": {
|
|
100
|
+
"command": "assay-mcp-server",
|
|
101
|
+
"args": ["--token", "YOUR_FIREBASE_TOKEN_HERE"]
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
### Using Environment Variable
|
|
108
|
+
|
|
109
|
+
```json
|
|
110
|
+
{
|
|
111
|
+
"mcpServers": {
|
|
112
|
+
"assay": {
|
|
113
|
+
"command": "assay-mcp-server",
|
|
114
|
+
"env": {
|
|
115
|
+
"ASSAY_FIREBASE_TOKEN": "YOUR_FIREBASE_TOKEN_HERE",
|
|
116
|
+
"CLAUDE_API_KEY": "YOUR_CLAUDE_API_KEY_HERE"
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
**Optional Environment Variables:**
|
|
124
|
+
- `FIREBASE_API_KEY` - Override Firebase Web API key (default: Assay service key)
|
|
125
|
+
- Only needed if you want to use your own Firebase project
|
|
126
|
+
- Get from: Firebase Console > Project Settings > General > Your apps
|
|
127
|
+
- `ASSAY_FUNCTIONS_BASE_URL` - Override Cloud Functions endpoint (default: `https://us-east4-pdfsummaries.cloudfunctions.net`)
|
|
128
|
+
- `CLAUDE_API_KEY` - For optional server-side synthesis
|
|
129
|
+
|
|
130
|
+
**⚠️ Optional: Server-Side Synthesis**
|
|
131
|
+
|
|
132
|
+
Server-side synthesis is **optional**. The MCP server works great without it!
|
|
133
|
+
|
|
134
|
+
**Without API key (Default):**
|
|
135
|
+
- Tools return raw data from your collection
|
|
136
|
+
- Claude Desktop synthesizes responses using the retrieved data
|
|
137
|
+
- Zero additional costs - you're already using Claude Desktop
|
|
138
|
+
|
|
139
|
+
**With API key (Optional):**
|
|
140
|
+
- Tools can return pre-synthesized responses using Claude API
|
|
141
|
+
- Requires your own Anthropic API key (set spending limits for safety)
|
|
142
|
+
- Add `CLAUDE_API_KEY` to your config (see [Setup Guide](./SETUP_GUIDE.md))
|
|
143
|
+
- Falls back to raw data if API key is missing or invalid
|
|
144
|
+
|
|
145
|
+
**See the [Complete Setup Guide](./SETUP_GUIDE.md) for detailed instructions on optional server-side synthesis.**
|
|
146
|
+
|
|
147
|
+
After updating the config file, **restart Claude Desktop** to load the MCP server.
|
|
148
|
+
|
|
149
|
+
## Available Tools
|
|
150
|
+
|
|
151
|
+
### Search Tools
|
|
152
|
+
|
|
153
|
+
#### 1. `search_documents`
|
|
154
|
+
Search your library by theme name, author, or document title. Searches both private and public collections.
|
|
155
|
+
|
|
156
|
+
**Example prompts:**
|
|
157
|
+
- "Search for documents about AI safety"
|
|
158
|
+
- "Find papers on zero trust architecture"
|
|
159
|
+
- "Show me documents by Anthropic researchers"
|
|
160
|
+
|
|
161
|
+
#### 2. `search_by_author`
|
|
162
|
+
Search specifically by author name.
|
|
163
|
+
|
|
164
|
+
**Example prompts:**
|
|
165
|
+
- "Find all documents by Brian Singer"
|
|
166
|
+
- "Show me papers from OpenAI researchers"
|
|
167
|
+
|
|
168
|
+
#### 3. `search_by_title`
|
|
169
|
+
Search by document title keywords.
|
|
170
|
+
|
|
171
|
+
**Example prompts:**
|
|
172
|
+
- "Find documents with 'red team' in the title"
|
|
173
|
+
- "Search for papers titled 'LLM safety'"
|
|
174
|
+
|
|
175
|
+
#### 4. `search_by_keywords`
|
|
176
|
+
Search in document keywords, key concepts, and key phrases.
|
|
177
|
+
|
|
178
|
+
**Example prompts:**
|
|
179
|
+
- "Find documents about 'machine learning' and 'neural networks'"
|
|
180
|
+
- "Search for 'zero trust' and 'confidential computing'"
|
|
181
|
+
|
|
182
|
+
### Document Retrieval
|
|
183
|
+
|
|
184
|
+
#### 5. `get_document_summary`
|
|
185
|
+
Get comprehensive, casual, or FAQ summaries for a specific document.
|
|
186
|
+
|
|
187
|
+
**Parameters:**
|
|
188
|
+
- `documentId`: The document ID
|
|
189
|
+
- `summaryType`: `"comprehensive"`, `"casual"`, or `"faq"`
|
|
190
|
+
|
|
191
|
+
**Example prompts:**
|
|
192
|
+
- "Get the comprehensive summary of document abc123"
|
|
193
|
+
- "What's the casual summary for the governance paper?"
|
|
194
|
+
- "Show me FAQs for document xyz789"
|
|
195
|
+
|
|
196
|
+
#### 6. `get_similar_documents`
|
|
197
|
+
Find documents similar to a given document using Jaccard similarity based on theme overlap.
|
|
198
|
+
|
|
199
|
+
**Returns:** Similar documents with similarity scores (High ≥35%, Moderate ≥20%, Low <20%)
|
|
200
|
+
|
|
201
|
+
**Example prompts:**
|
|
202
|
+
- "What documents are similar to document abc123?"
|
|
203
|
+
- "Find related papers to the prompt injection document"
|
|
204
|
+
|
|
205
|
+
### Exploration Tools
|
|
206
|
+
|
|
207
|
+
#### 7. `browse_themes`
|
|
208
|
+
Browse the canonical theme taxonomy. Without a domain, returns all L0 domains with document counts. With a domain, returns L1 themes within that domain.
|
|
209
|
+
|
|
210
|
+
**Example prompts:**
|
|
211
|
+
- "What themes do I have documents in?"
|
|
212
|
+
- "Show me all themes under Cybersecurity"
|
|
213
|
+
- "Browse themes in the Artificial Intelligence domain"
|
|
214
|
+
|
|
215
|
+
#### 8. `get_library_insight`
|
|
216
|
+
Get your AI-generated personalized insight about your research profile.
|
|
217
|
+
|
|
218
|
+
**Example prompts:**
|
|
219
|
+
- "What does my document collection say about my research interests?"
|
|
220
|
+
- "Get my library insight"
|
|
221
|
+
|
|
222
|
+
### Advanced Analysis
|
|
223
|
+
|
|
224
|
+
#### 9. `ask_question`
|
|
225
|
+
Ask a question about your library. The tool searches for relevant documents and returns their summaries for Claude to synthesize an answer.
|
|
226
|
+
|
|
227
|
+
**With Claude API key:** Returns a synthesized answer (server-side).
|
|
228
|
+
**Without API key:** Returns raw document summaries for Claude Desktop to synthesize.
|
|
229
|
+
|
|
230
|
+
**Example prompts:**
|
|
231
|
+
- "If I wanted to automate Red Team exercises, what should I consider?"
|
|
232
|
+
- "What are the key considerations for implementing zero trust?"
|
|
233
|
+
- "What do my documents say about AI safety frameworks?"
|
|
234
|
+
|
|
235
|
+
#### 10. `compare_documents`
|
|
236
|
+
Compare up to 25 documents selected by themes, keywords, or authors. Returns documents sorted by relevance score.
|
|
237
|
+
|
|
238
|
+
**With Claude API key:** Returns a synthesized comparison (server-side).
|
|
239
|
+
**Without API key:** Returns raw document summaries for Claude Desktop to synthesize.
|
|
240
|
+
|
|
241
|
+
**Parameters:**
|
|
242
|
+
- `themes`: Array of theme IDs (e.g., `["CYBERSECURITY", "ARTIFICIAL_INTELLIGENCE.AI_ARCHITECTURES"]`)
|
|
243
|
+
- `keywords`: Array of keywords
|
|
244
|
+
- `authors`: Array of author names
|
|
245
|
+
|
|
246
|
+
**Example prompts:**
|
|
247
|
+
- "Compare documents about AI safety and cybersecurity"
|
|
248
|
+
- "Compare papers by Anthropic and OpenAI on AI alignment"
|
|
249
|
+
|
|
250
|
+
#### 11. `produce_faq`
|
|
251
|
+
Generate FAQs from up to 25 documents selected by themes, keywords, or authors.
|
|
252
|
+
|
|
253
|
+
**With Claude API key:** Returns a synthesized FAQ (server-side).
|
|
254
|
+
**Without API key:** Returns raw document FAQs for Claude Desktop to synthesize.
|
|
255
|
+
|
|
256
|
+
**Parameters:** Same as `compare_documents`
|
|
257
|
+
|
|
258
|
+
**Example prompts:**
|
|
259
|
+
- "Generate FAQs about AI architectures and cybersecurity"
|
|
260
|
+
- "Create FAQs from documents about zero trust by these authors"
|
|
261
|
+
|
|
262
|
+
## How It Compares
|
|
263
|
+
|
|
264
|
+
The Assay MCP Server offers unique advantages over traditional RAG systems and file-based AI assistants:
|
|
265
|
+
|
|
266
|
+
| Feature | Assay MCP | Traditional RAG | ChatGPT with Files |
|
|
267
|
+
|---------|-----------|-----------------|-------------------|
|
|
268
|
+
| **Pre-processed summaries** | ✅ 3 formats (comprehensive, casual, FAQs) | ❌ Processes on-the-fly | ❌ Processes on-the-fly |
|
|
269
|
+
| **Theme-based organization** | ✅ 180 canonical themes | ❌ No structured taxonomy | ❌ No structured taxonomy |
|
|
270
|
+
| **Similarity algorithm** | ✅ Jaccard (theme overlap) | Vector embeddings | Vector embeddings |
|
|
271
|
+
| **Multi-format summaries** | ✅ Comprehensive, Casual, FAQs | Single format | Single format |
|
|
272
|
+
| **Cost per query** | ✅ $0 (Firestore only) | $0.01-0.10 (embeddings + LLM) | Included in subscription |
|
|
273
|
+
| **Conversational interface** | ✅ Via Claude Desktop | ✅ Via chat interface | ✅ Native |
|
|
274
|
+
| **Public + Private collections** | ✅ Both searchable | ❌ Single collection | ❌ Single collection |
|
|
275
|
+
| **Cross-document analysis** | ✅ Compare, synthesize FAQs | Limited | Limited |
|
|
276
|
+
| **Structured metadata** | ✅ Authors, dates, keywords | ❌ Unstructured | ❌ Unstructured |
|
|
277
|
+
| **No document storage** | ✅ Summaries only | ❌ Stores full documents | ❌ Stores full documents |
|
|
278
|
+
|
|
279
|
+
### Key Advantages
|
|
280
|
+
|
|
281
|
+
**1. Zero-cost queries**
|
|
282
|
+
- Traditional RAG requires embedding generation and LLM calls for every query
|
|
283
|
+
- Assay MCP only queries Firestore (no AI costs)
|
|
284
|
+
- Claude does the synthesis (you're already paying for Claude)
|
|
285
|
+
|
|
286
|
+
**2. Pre-processed intelligence**
|
|
287
|
+
- Documents are analyzed once during upload
|
|
288
|
+
- Multiple summary formats available instantly
|
|
289
|
+
- No waiting for processing during queries
|
|
290
|
+
|
|
291
|
+
**3. Structured discovery**
|
|
292
|
+
- 180 canonical themes enable precise filtering
|
|
293
|
+
- Jaccard similarity finds documents with shared research areas
|
|
294
|
+
- Not just keyword matching - semantic theme understanding
|
|
295
|
+
|
|
296
|
+
**4. Multi-collection access**
|
|
297
|
+
- Search both your private documents and public collection
|
|
298
|
+
- Discover research from other users
|
|
299
|
+
- Privacy-respecting (only shows what you have access to)
|
|
300
|
+
|
|
301
|
+
## How It Works
|
|
302
|
+
|
|
303
|
+
### Relevance Scoring
|
|
304
|
+
|
|
305
|
+
All tools use **Jaccard similarity** for relevance scoring:
|
|
306
|
+
|
|
307
|
+
- **Themes**: Weighted Jaccard (L1 themes = 0.8, L0 domains = 0.2)
|
|
308
|
+
- **Keywords**: Standard Jaccard similarity
|
|
309
|
+
- **Authors**: Standard Jaccard similarity
|
|
310
|
+
- **Combined**: Weighted average (themes = 0.6, keywords = 0.25, authors = 0.15)
|
|
311
|
+
|
|
312
|
+
**Similarity Levels:**
|
|
313
|
+
- **High**: ≥35% similarity
|
|
314
|
+
- **Moderate**: ≥20% similarity
|
|
315
|
+
- **Low**: <20% similarity
|
|
316
|
+
|
|
317
|
+
### Data Flow
|
|
318
|
+
|
|
319
|
+
1. **Tool Call**: Claude calls an MCP tool with parameters
|
|
320
|
+
2. **Authentication**: Server verifies Firebase ID token
|
|
321
|
+
3. **Query**: Searches Firestore for matching documents
|
|
322
|
+
4. **Scoring**: Calculates Jaccard similarity scores
|
|
323
|
+
5. **Retrieval**: Fetches document summaries and metadata
|
|
324
|
+
6. **Return**: Structured JSON with documents and summaries
|
|
325
|
+
7. **Synthesis**: Claude uses the data to answer questions or generate insights
|
|
326
|
+
|
|
327
|
+
### Privacy & Security
|
|
328
|
+
|
|
329
|
+
- **Token-based authentication**: Each request requires a valid Firebase ID token
|
|
330
|
+
- **User-scoped queries**: Only returns documents the user has access to
|
|
331
|
+
- **Private + Public**: Searches both collections but respects visibility settings
|
|
332
|
+
- **No document storage**: Tools only retrieve summaries, not full document content
|
|
333
|
+
|
|
334
|
+
## Development
|
|
335
|
+
|
|
336
|
+
### Setup
|
|
337
|
+
|
|
338
|
+
```bash
|
|
339
|
+
# Install dependencies
|
|
340
|
+
npm install
|
|
341
|
+
|
|
342
|
+
# Build TypeScript
|
|
343
|
+
npm run build
|
|
344
|
+
|
|
345
|
+
# Run in development mode
|
|
346
|
+
npm run dev -- --token <your-token>
|
|
347
|
+
```
|
|
348
|
+
|
|
349
|
+
### Project Structure
|
|
350
|
+
|
|
351
|
+
```
|
|
352
|
+
assay-mcp-server/
|
|
353
|
+
├── src/
|
|
354
|
+
│ ├── index.ts # Main MCP server entry point
|
|
355
|
+
│ ├── firebase.ts # Firebase REST API integration (no Admin SDK needed!)
|
|
356
|
+
│ └── tools/ # Individual tool implementations
|
|
357
|
+
│ ├── searchDocuments.ts
|
|
358
|
+
│ ├── getDocumentSummary.ts
|
|
359
|
+
│ ├── getSimilarDocuments.ts
|
|
360
|
+
│ ├── searchByAuthor.ts
|
|
361
|
+
│ ├── searchByTitle.ts
|
|
362
|
+
│ ├── searchByKeywords.ts
|
|
363
|
+
│ ├── getLibraryInsight.ts
|
|
364
|
+
│ ├── browseThemes.ts
|
|
365
|
+
│ ├── askQuestion.ts
|
|
366
|
+
│ ├── compareDocuments.ts
|
|
367
|
+
│ ├── produceFaq.ts
|
|
368
|
+
│ └── scoringUtils.ts # Shared scoring functions
|
|
369
|
+
├── dist/ # Compiled JavaScript
|
|
370
|
+
└── package.json
|
|
371
|
+
```
|
|
372
|
+
|
|
373
|
+
### Adding New Tools
|
|
374
|
+
|
|
375
|
+
1. Create a new file in `src/tools/`
|
|
376
|
+
2. Export a `Tool` definition and implementation function
|
|
377
|
+
3. Import and register in `src/index.ts`
|
|
378
|
+
4. Rebuild: `npm run build`
|
|
379
|
+
|
|
380
|
+
## Troubleshooting
|
|
381
|
+
|
|
382
|
+
### Server Disconnects
|
|
383
|
+
|
|
384
|
+
- **Token expired**: Generate a new token from the Assay dashboard (tokens expire after 1 hour)
|
|
385
|
+
- **Invalid token**: Make sure you copied the full token without extra characters
|
|
386
|
+
- **Path issues**: Verify the absolute path in Claude Desktop config is correct
|
|
387
|
+
|
|
388
|
+
### No Results Returned
|
|
389
|
+
|
|
390
|
+
- **Check authentication**: Ensure you're signed in to Assay
|
|
391
|
+
- **Verify documents**: Make sure you have documents uploaded and processed
|
|
392
|
+
- **Check visibility**: Public documents are searchable, private documents only by owner
|
|
393
|
+
|
|
394
|
+
### Build Errors
|
|
395
|
+
|
|
396
|
+
- **Node version**: Ensure Node.js 18+ is installed
|
|
397
|
+
- **Dependencies**: Run `npm install` to ensure all packages are installed
|
|
398
|
+
- **TypeScript**: Check `tsconfig.json` is properly configured
|
|
399
|
+
|
|
400
|
+
## Contributing
|
|
401
|
+
|
|
402
|
+
Contributions welcome! Please open an issue or submit a pull request.
|
|
403
|
+
|
|
404
|
+
## License
|
|
405
|
+
|
|
406
|
+
MIT License - see LICENSE file for details
|
|
407
|
+
|
|
408
|
+
## Links
|
|
409
|
+
|
|
410
|
+
- **Assay Web App**: https://assay.cirrusly-clever.com
|
|
411
|
+
- **MCP Documentation**: https://modelcontextprotocol.io
|
|
412
|
+
|
|
413
|
+
## Support
|
|
414
|
+
|
|
415
|
+
For issues, questions, or feature requests:
|
|
416
|
+
- Open an issue on GitHub
|
|
417
|
+
- Contact: support@cirrusly-clever.com
|
|
418
|
+
|
|
419
|
+
---
|
|
420
|
+
|
|
421
|
+
**Made with ❤️ by [Cirrusly Clever](https://cirrusly-clever.com)**
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Firebase integration using Cloud Function proxies
|
|
3
|
+
* MCP server is now a thin client that calls Assay Cloud Functions
|
|
4
|
+
* No Firestore access needed - all handled by backend!
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Verify Firebase ID token using Firebase Auth REST API
|
|
8
|
+
*/
|
|
9
|
+
export declare function verifyToken(token: string): Promise<{
|
|
10
|
+
uid: string;
|
|
11
|
+
email?: string;
|
|
12
|
+
}>;
|
|
13
|
+
/**
|
|
14
|
+
* Get Claude API key from environment
|
|
15
|
+
*/
|
|
16
|
+
export declare function getClaudeApiKey(): string | undefined;
|
|
17
|
+
/**
|
|
18
|
+
* Call a Cloud Function with user's ID token
|
|
19
|
+
*/
|
|
20
|
+
export declare function callCloudFunction(functionName: string, data: any, token: string): Promise<any>;
|
|
21
|
+
/**
|
|
22
|
+
* Firestore wrapper that calls Cloud Functions instead
|
|
23
|
+
* This maintains the same API so existing code doesn't break
|
|
24
|
+
*/
|
|
25
|
+
export declare function getFirestore(token: string): {
|
|
26
|
+
collection: (collectionName: string) => {
|
|
27
|
+
where: (field: string, operator: string, value: any) => {
|
|
28
|
+
where: (field2: string, operator2: string, value2: any) => {
|
|
29
|
+
get: () => Promise<{
|
|
30
|
+
docs: never[];
|
|
31
|
+
}>;
|
|
32
|
+
};
|
|
33
|
+
get: () => Promise<{
|
|
34
|
+
docs: never[];
|
|
35
|
+
}>;
|
|
36
|
+
};
|
|
37
|
+
doc: (docId: string) => {
|
|
38
|
+
get: () => Promise<{
|
|
39
|
+
exists: boolean;
|
|
40
|
+
id: string;
|
|
41
|
+
data: () => {};
|
|
42
|
+
}>;
|
|
43
|
+
};
|
|
44
|
+
get: () => Promise<{
|
|
45
|
+
docs: never[];
|
|
46
|
+
}>;
|
|
47
|
+
};
|
|
48
|
+
};
|
|
49
|
+
/**
|
|
50
|
+
* Initialize Firebase (no-op for this approach)
|
|
51
|
+
*/
|
|
52
|
+
export declare function initializeFirebase(): void;
|
|
53
|
+
//# sourceMappingURL=firebase.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"firebase.d.ts","sourceRoot":"","sources":["../src/firebase.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAYH;;GAEG;AACH,wBAAsB,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,CA+BzF;AAED;;GAEG;AACH,wBAAgB,eAAe,IAAI,MAAM,GAAG,SAAS,CAEpD;AAED;;GAEG;AACH,wBAAsB,iBAAiB,CACrC,YAAY,EAAE,MAAM,EACpB,IAAI,EAAE,GAAG,EACT,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC,GAAG,CAAC,CAwCd;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM;iCAET,MAAM;uBAClB,MAAM,YAAY,MAAM,SAAS,GAAG;4BACjC,MAAM,aAAa,MAAM,UAAU,GAAG;;;;;;;;;qBAW3C,MAAM;;;;;;;;;;;EAUxB;AAID;;GAEG;AACH,wBAAgB,kBAAkB,IAAI,IAAI,CAEzC"}
|
package/dist/firebase.js
ADDED
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Firebase integration using Cloud Function proxies
|
|
3
|
+
* MCP server is now a thin client that calls Assay Cloud Functions
|
|
4
|
+
* No Firestore access needed - all handled by backend!
|
|
5
|
+
*/
|
|
6
|
+
const ASSAY_FUNCTIONS_BASE_URL = process.env.ASSAY_FUNCTIONS_BASE_URL ||
|
|
7
|
+
'https://us-east4-pdfsummaries.cloudfunctions.net';
|
|
8
|
+
// Firebase Web API key for token verification
|
|
9
|
+
// This is a PUBLIC API key (Firebase Web API keys are designed to be public)
|
|
10
|
+
// Security is provided by API restrictions in Google Cloud Console, not by keeping it secret
|
|
11
|
+
// Users can override with FIREBASE_API_KEY env var if they want to use their own Firebase project
|
|
12
|
+
const FIREBASE_API_KEY = process.env.FIREBASE_API_KEY ||
|
|
13
|
+
'AIzaSyBBq7eyl1vvcZWTdM1CmnPjlcX0OEsr5Ws';
|
|
14
|
+
/**
|
|
15
|
+
* Verify Firebase ID token using Firebase Auth REST API
|
|
16
|
+
*/
|
|
17
|
+
export async function verifyToken(token) {
|
|
18
|
+
try {
|
|
19
|
+
const response = await fetch(`https://identitytoolkit.googleapis.com/v1/accounts:lookup?key=${FIREBASE_API_KEY}`, {
|
|
20
|
+
method: 'POST',
|
|
21
|
+
headers: {
|
|
22
|
+
'Content-Type': 'application/json',
|
|
23
|
+
},
|
|
24
|
+
body: JSON.stringify({ idToken: token }),
|
|
25
|
+
});
|
|
26
|
+
if (!response.ok) {
|
|
27
|
+
const errorData = (await response.json().catch(() => ({})));
|
|
28
|
+
throw new Error(`Token verification failed: ${errorData.error?.message || response.statusText}`);
|
|
29
|
+
}
|
|
30
|
+
const data = (await response.json());
|
|
31
|
+
if (!data.users || data.users.length === 0) {
|
|
32
|
+
throw new Error('Invalid token: no user found');
|
|
33
|
+
}
|
|
34
|
+
const user = data.users[0];
|
|
35
|
+
return {
|
|
36
|
+
uid: user.localId,
|
|
37
|
+
email: user.email,
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
catch (error) {
|
|
41
|
+
throw new Error(`Token verification failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Get Claude API key from environment
|
|
46
|
+
*/
|
|
47
|
+
export function getClaudeApiKey() {
|
|
48
|
+
return process.env.CLAUDE_API_KEY || process.env.ANTHROPIC_API_KEY;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Call a Cloud Function with user's ID token
|
|
52
|
+
*/
|
|
53
|
+
export async function callCloudFunction(functionName, data, token) {
|
|
54
|
+
// Include Claude API key if available
|
|
55
|
+
const claudeApiKey = getClaudeApiKey();
|
|
56
|
+
if (claudeApiKey) {
|
|
57
|
+
data.claudeApiKey = claudeApiKey;
|
|
58
|
+
}
|
|
59
|
+
const url = `${ASSAY_FUNCTIONS_BASE_URL}/${functionName}`;
|
|
60
|
+
const response = await fetch(url, {
|
|
61
|
+
method: 'POST',
|
|
62
|
+
headers: {
|
|
63
|
+
'Authorization': `Bearer ${token}`,
|
|
64
|
+
'Content-Type': 'application/json',
|
|
65
|
+
},
|
|
66
|
+
body: JSON.stringify({ data }),
|
|
67
|
+
});
|
|
68
|
+
if (!response.ok) {
|
|
69
|
+
const errorText = await response.text();
|
|
70
|
+
let errorData;
|
|
71
|
+
try {
|
|
72
|
+
errorData = JSON.parse(errorText);
|
|
73
|
+
}
|
|
74
|
+
catch {
|
|
75
|
+
errorData = { error: { message: errorText } };
|
|
76
|
+
}
|
|
77
|
+
throw new Error(`Cloud Function ${functionName} failed: ${errorData.error?.message || response.statusText} (${response.status})`);
|
|
78
|
+
}
|
|
79
|
+
const result = (await response.json());
|
|
80
|
+
// Firebase Callable Functions return { result: ... } or { error: ... }
|
|
81
|
+
if (result.error) {
|
|
82
|
+
throw new Error(`Cloud Function error: ${result.error.message || JSON.stringify(result.error)}`);
|
|
83
|
+
}
|
|
84
|
+
return result.result;
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Firestore wrapper that calls Cloud Functions instead
|
|
88
|
+
* This maintains the same API so existing code doesn't break
|
|
89
|
+
*/
|
|
90
|
+
export function getFirestore(token) {
|
|
91
|
+
return {
|
|
92
|
+
collection: (collectionName) => ({
|
|
93
|
+
where: (field, operator, value) => ({
|
|
94
|
+
where: (field2, operator2, value2) => ({
|
|
95
|
+
get: async () => {
|
|
96
|
+
// This is a simplified wrapper - actual queries go through Cloud Functions
|
|
97
|
+
// For now, return empty - individual tools will call Cloud Functions directly
|
|
98
|
+
return { docs: [] };
|
|
99
|
+
},
|
|
100
|
+
}),
|
|
101
|
+
get: async () => {
|
|
102
|
+
return { docs: [] };
|
|
103
|
+
},
|
|
104
|
+
}),
|
|
105
|
+
doc: (docId) => ({
|
|
106
|
+
get: async () => {
|
|
107
|
+
return { exists: false, id: docId, data: () => ({}) };
|
|
108
|
+
},
|
|
109
|
+
}),
|
|
110
|
+
get: async () => {
|
|
111
|
+
return { docs: [] };
|
|
112
|
+
},
|
|
113
|
+
}),
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
// callCloudFunction is already exported above
|
|
117
|
+
/**
|
|
118
|
+
* Initialize Firebase (no-op for this approach)
|
|
119
|
+
*/
|
|
120
|
+
export function initializeFirebase() {
|
|
121
|
+
// No initialization needed - we use Cloud Functions
|
|
122
|
+
}
|
|
123
|
+
//# sourceMappingURL=firebase.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"firebase.js","sourceRoot":"","sources":["../src/firebase.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":""}
|