chore: @modelcontextprotocol/sdk als Abhängigkeit hinzugefügt

This commit is contained in:
sebseb7
2026-04-05 03:20:20 +02:00
parent 38e702ec2f
commit 863f45f666
8 changed files with 539 additions and 10 deletions

99
README.md Normal file
View File

@@ -0,0 +1,99 @@
# Search Agent
An intelligent search service with REST API, CLI, and MCP server interfaces.
## Features
- **REST API**: Full-featured HTTP API with streaming support
- **CLI**: Command-line interface for quick searches
- **MCP Server**: Model Context Protocol server for integration with AI assistants
## Installation
```bash
npm install
```
## Usage
### REST API Server
```bash
npm run serve
```
The server will start at `http://localhost:3000`.
### CLI
```bash
npm run search "Your question here"
```
### MCP Server
```bash
npm run mcp
```
## MCP Configuration
To use the search agent as an MCP server in your AI assistant, add this to your configuration:
### Claude Desktop
Add to `claude_desktop_config.json`:
```json
{
"mcpServers": {
"search-agent": {
"command": "node",
"args": ["/path/to/searchAgent/mcpServer.js"],
"env": {
"EXA_API_KEY": "your-exa-api-key",
"OPENROUTER_API_KEY": "your-openrouter-api-key"
}
}
}
}
```
### Other MCP Clients
For other MCP clients, configure the command as:
- Command: `node`
- Args: `["/path/to/searchAgent/mcpServer.js"]`
- Environment variables: `EXA_API_KEY` and `OPENROUTER_API_KEY`
## Available MCP Tools
### search
Search for information and return a summarized answer with sources.
**Parameters:**
- `question` (required): The question or topic to search for
- `previousClarification` (optional): Previous clarification from the user if the question needed refinement
- `originalQuestion` (optional): The original question if this is a follow-up
**Example:**
```json
{
"name": "search",
"arguments": {
"question": "What are the latest developments in AI?"
}
}
```
## Environment Variables
Required:
- `EXA_API_KEY`: Your Exa API key
- `OPENROUTER_API_KEY`: Your OpenRouter API key
Optional:
- `PORT`: Port for REST server (default: 3000)
- `HOST`: Host for REST server (default: 0.0.0.0)
- `MAINTENANCE_MODE`: Set to "true" to enable maintenance mode

11
mcpServer.js Executable file
View File

@@ -0,0 +1,11 @@
#!/home/seb/.nvm/versions/node/v22.15.1/bin/node
import dotenv from 'dotenv';
import { startMCPServer } from './src/mcpServer.js';
// Load environment variables from .env file
dotenv.config();
startMCPServer().catch((error) => {
console.error('Failed to start MCP server:', error);
process.exit(1);
});

295
package-lock.json generated
View File

@@ -9,6 +9,7 @@
"version": "1.0.0", "version": "1.0.0",
"license": "ISC", "license": "ISC",
"dependencies": { "dependencies": {
"@modelcontextprotocol/sdk": "^1.0.4",
"@openrouter/sdk": "^0.11.2", "@openrouter/sdk": "^0.11.2",
"chalk": "^5.6.2", "chalk": "^5.6.2",
"dotenv": "^17.2.3", "dotenv": "^17.2.3",
@@ -17,6 +18,58 @@
"tiktoken": "^1.0.22" "tiktoken": "^1.0.22"
} }
}, },
"node_modules/@hono/node-server": {
"version": "1.19.12",
"resolved": "https://registry.npmjs.org/@hono/node-server/-/node-server-1.19.12.tgz",
"integrity": "sha512-txsUW4SQ1iilgE0l9/e9VQWmELXifEFvmdA1j6WFh/aFPj99hIntrSsq/if0UWyGVkmrRPKA1wCeP+UCr1B9Uw==",
"license": "MIT",
"engines": {
"node": ">=18.14.1"
},
"peerDependencies": {
"hono": "^4"
}
},
"node_modules/@modelcontextprotocol/sdk": {
"version": "1.29.0",
"resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.29.0.tgz",
"integrity": "sha512-zo37mZA9hJWpULgkRpowewez1y6ML5GsXJPY8FI0tBBCd77HEvza4jDqRKOXgHNn867PVGCyTdzqpz0izu5ZjQ==",
"license": "MIT",
"dependencies": {
"@hono/node-server": "^1.19.9",
"ajv": "^8.17.1",
"ajv-formats": "^3.0.1",
"content-type": "^1.0.5",
"cors": "^2.8.5",
"cross-spawn": "^7.0.5",
"eventsource": "^3.0.2",
"eventsource-parser": "^3.0.0",
"express": "^5.2.1",
"express-rate-limit": "^8.2.1",
"hono": "^4.11.4",
"jose": "^6.1.3",
"json-schema-typed": "^8.0.2",
"pkce-challenge": "^5.0.0",
"raw-body": "^3.0.0",
"zod": "^3.25 || ^4.0",
"zod-to-json-schema": "^3.25.1"
},
"engines": {
"node": ">=18"
},
"peerDependencies": {
"@cfworker/json-schema": "^4.1.1",
"zod": "^3.25 || ^4.0"
},
"peerDependenciesMeta": {
"@cfworker/json-schema": {
"optional": true
},
"zod": {
"optional": false
}
}
},
"node_modules/@openrouter/sdk": { "node_modules/@openrouter/sdk": {
"version": "0.11.2", "version": "0.11.2",
"resolved": "https://registry.npmjs.org/@openrouter/sdk/-/sdk-0.11.2.tgz", "resolved": "https://registry.npmjs.org/@openrouter/sdk/-/sdk-0.11.2.tgz",
@@ -40,6 +93,39 @@
"node": ">= 0.6" "node": ">= 0.6"
} }
}, },
"node_modules/ajv": {
"version": "8.18.0",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz",
"integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==",
"license": "MIT",
"dependencies": {
"fast-deep-equal": "^3.1.3",
"fast-uri": "^3.0.1",
"json-schema-traverse": "^1.0.0",
"require-from-string": "^2.0.2"
},
"funding": {
"type": "github",
"url": "https://github.com/sponsors/epoberezkin"
}
},
"node_modules/ajv-formats": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz",
"integrity": "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==",
"license": "MIT",
"dependencies": {
"ajv": "^8.0.0"
},
"peerDependencies": {
"ajv": "^8.0.0"
},
"peerDependenciesMeta": {
"ajv": {
"optional": true
}
}
},
"node_modules/body-parser": { "node_modules/body-parser": {
"version": "2.2.2", "version": "2.2.2",
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.2.tgz", "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.2.tgz",
@@ -154,6 +240,23 @@
"node": ">=6.6.0" "node": ">=6.6.0"
} }
}, },
"node_modules/cors": {
"version": "2.8.6",
"resolved": "https://registry.npmjs.org/cors/-/cors-2.8.6.tgz",
"integrity": "sha512-tJtZBBHA6vjIAaF6EnIaq6laBBP9aq/Y3ouVJjEfoHbRBcHBAHYcMh/w8LDrk2PvIMMq8gmopa5D4V8RmbrxGw==",
"license": "MIT",
"dependencies": {
"object-assign": "^4",
"vary": "^1"
},
"engines": {
"node": ">= 0.10"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/express"
}
},
"node_modules/cross-fetch": { "node_modules/cross-fetch": {
"version": "4.1.0", "version": "4.1.0",
"resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-4.1.0.tgz", "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-4.1.0.tgz",
@@ -163,6 +266,20 @@
"node-fetch": "^2.7.0" "node-fetch": "^2.7.0"
} }
}, },
"node_modules/cross-spawn": {
"version": "7.0.6",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
"integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==",
"license": "MIT",
"dependencies": {
"path-key": "^3.1.0",
"shebang-command": "^2.0.0",
"which": "^2.0.1"
},
"engines": {
"node": ">= 8"
}
},
"node_modules/debug": { "node_modules/debug": {
"version": "4.4.3", "version": "4.4.3",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
@@ -275,6 +392,27 @@
"node": ">= 0.6" "node": ">= 0.6"
} }
}, },
"node_modules/eventsource": {
"version": "3.0.7",
"resolved": "https://registry.npmjs.org/eventsource/-/eventsource-3.0.7.tgz",
"integrity": "sha512-CRT1WTyuQoD771GW56XEZFQ/ZoSfWid1alKGDYMmkt2yl8UXrVR4pspqWNEcqKvVIzg6PAltWjxcSSPrboA4iA==",
"license": "MIT",
"dependencies": {
"eventsource-parser": "^3.0.1"
},
"engines": {
"node": ">=18.0.0"
}
},
"node_modules/eventsource-parser": {
"version": "3.0.6",
"resolved": "https://registry.npmjs.org/eventsource-parser/-/eventsource-parser-3.0.6.tgz",
"integrity": "sha512-Vo1ab+QXPzZ4tCa8SwIHJFaSzy4R6SHf7BY79rFBDf0idraZWAkYrDjDj8uWaSm3S2TK+hJ7/t1CEmZ7jXw+pg==",
"license": "MIT",
"engines": {
"node": ">=18.0.0"
}
},
"node_modules/exa-js": { "node_modules/exa-js": {
"version": "2.11.0", "version": "2.11.0",
"resolved": "https://registry.npmjs.org/exa-js/-/exa-js-2.11.0.tgz", "resolved": "https://registry.npmjs.org/exa-js/-/exa-js-2.11.0.tgz",
@@ -343,6 +481,46 @@
"url": "https://opencollective.com/express" "url": "https://opencollective.com/express"
} }
}, },
"node_modules/express-rate-limit": {
"version": "8.3.2",
"resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-8.3.2.tgz",
"integrity": "sha512-77VmFeJkO0/rvimEDuUC5H30oqUC4EyOhyGccfqoLebB0oiEYfM7nwPrsDsBL1gsTpwfzX8SFy2MT3TDyRq+bg==",
"license": "MIT",
"dependencies": {
"ip-address": "10.1.0"
},
"engines": {
"node": ">= 16"
},
"funding": {
"url": "https://github.com/sponsors/express-rate-limit"
},
"peerDependencies": {
"express": ">= 4.11"
}
},
"node_modules/fast-deep-equal": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
"license": "MIT"
},
"node_modules/fast-uri": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.0.tgz",
"integrity": "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/fastify"
},
{
"type": "opencollective",
"url": "https://opencollective.com/fastify"
}
],
"license": "BSD-3-Clause"
},
"node_modules/finalhandler": { "node_modules/finalhandler": {
"version": "2.1.1", "version": "2.1.1",
"resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.1.tgz", "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.1.tgz",
@@ -464,6 +642,15 @@
"node": ">= 0.4" "node": ">= 0.4"
} }
}, },
"node_modules/hono": {
"version": "4.12.10",
"resolved": "https://registry.npmjs.org/hono/-/hono-4.12.10.tgz",
"integrity": "sha512-mx/p18PLy5og9ufies2GOSUqep98Td9q4i/EF6X7yJgAiIopxqdfIO3jbqsi3jRgTgw88jMDEzVKi+V2EF+27w==",
"license": "MIT",
"engines": {
"node": ">=16.9.0"
}
},
"node_modules/http-errors": { "node_modules/http-errors": {
"version": "2.0.1", "version": "2.0.1",
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz",
@@ -506,6 +693,15 @@
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
"license": "ISC" "license": "ISC"
}, },
"node_modules/ip-address": {
"version": "10.1.0",
"resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.1.0.tgz",
"integrity": "sha512-XXADHxXmvT9+CRxhXg56LJovE+bmWnEWB78LB83VZTprKTmaC5QfruXocxzTZ2Kl0DNwKuBdlIhjL8LeY8Sf8Q==",
"license": "MIT",
"engines": {
"node": ">= 12"
}
},
"node_modules/ipaddr.js": { "node_modules/ipaddr.js": {
"version": "1.9.1", "version": "1.9.1",
"resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
@@ -521,6 +717,33 @@
"integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==", "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==",
"license": "MIT" "license": "MIT"
}, },
"node_modules/isexe": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
"integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
"license": "ISC"
},
"node_modules/jose": {
"version": "6.2.2",
"resolved": "https://registry.npmjs.org/jose/-/jose-6.2.2.tgz",
"integrity": "sha512-d7kPDd34KO/YnzaDOlikGpOurfF0ByC2sEV4cANCtdqLlTfBlw2p14O/5d/zv40gJPbIQxfES3nSx1/oYNyuZQ==",
"license": "MIT",
"funding": {
"url": "https://github.com/sponsors/panva"
}
},
"node_modules/json-schema-traverse": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
"integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
"license": "MIT"
},
"node_modules/json-schema-typed": {
"version": "8.0.2",
"resolved": "https://registry.npmjs.org/json-schema-typed/-/json-schema-typed-8.0.2.tgz",
"integrity": "sha512-fQhoXdcvc3V28x7C7BMs4P5+kNlgUURe2jmUT1T//oBRMDrqy1QPelJimwZGo7Hg9VPV3EQV5Bnq4hbFy2vetA==",
"license": "BSD-2-Clause"
},
"node_modules/math-intrinsics": { "node_modules/math-intrinsics": {
"version": "1.1.0", "version": "1.1.0",
"resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
@@ -611,6 +834,15 @@
} }
} }
}, },
"node_modules/object-assign": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
"integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
"license": "MIT",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/object-inspect": { "node_modules/object-inspect": {
"version": "1.13.4", "version": "1.13.4",
"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz",
@@ -674,6 +906,15 @@
"node": ">= 0.8" "node": ">= 0.8"
} }
}, },
"node_modules/path-key": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
"integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
"license": "MIT",
"engines": {
"node": ">=8"
}
},
"node_modules/path-to-regexp": { "node_modules/path-to-regexp": {
"version": "8.4.2", "version": "8.4.2",
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.4.2.tgz", "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.4.2.tgz",
@@ -684,6 +925,15 @@
"url": "https://opencollective.com/express" "url": "https://opencollective.com/express"
} }
}, },
"node_modules/pkce-challenge": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/pkce-challenge/-/pkce-challenge-5.0.1.tgz",
"integrity": "sha512-wQ0b/W4Fr01qtpHlqSqspcj3EhBvimsdh0KlHhH8HRZnMsEa0ea2fTULOXOS9ccQr3om+GcGRk4e+isrZWV8qQ==",
"license": "MIT",
"engines": {
"node": ">=16.20.0"
}
},
"node_modules/proxy-addr": { "node_modules/proxy-addr": {
"version": "2.0.7", "version": "2.0.7",
"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
@@ -736,6 +986,15 @@
"node": ">= 0.10" "node": ">= 0.10"
} }
}, },
"node_modules/require-from-string": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz",
"integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==",
"license": "MIT",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/router": { "node_modules/router": {
"version": "2.2.0", "version": "2.2.0",
"resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz", "resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz",
@@ -809,6 +1068,27 @@
"integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==",
"license": "ISC" "license": "ISC"
}, },
"node_modules/shebang-command": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
"integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
"license": "MIT",
"dependencies": {
"shebang-regex": "^3.0.0"
},
"engines": {
"node": ">=8"
}
},
"node_modules/shebang-regex": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
"integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
"license": "MIT",
"engines": {
"node": ">=8"
}
},
"node_modules/side-channel": { "node_modules/side-channel": {
"version": "1.1.0", "version": "1.1.0",
"resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz",
@@ -959,6 +1239,21 @@
"webidl-conversions": "^3.0.0" "webidl-conversions": "^3.0.0"
} }
}, },
"node_modules/which": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
"integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
"license": "ISC",
"dependencies": {
"isexe": "^2.0.0"
},
"bin": {
"node-which": "bin/node-which"
},
"engines": {
"node": ">= 8"
}
},
"node_modules/wrappy": { "node_modules/wrappy": {
"version": "1.0.2", "version": "1.0.2",
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",

View File

@@ -6,12 +6,14 @@
"scripts": { "scripts": {
"serve": "node restSearch.js", "serve": "node restSearch.js",
"search": "node searchCLI.js", "search": "node searchCLI.js",
"mcp": "node mcpServer.js",
"test": "echo \"Error: no test specified\" && exit 1" "test": "echo \"Error: no test specified\" && exit 1"
}, },
"author": "", "author": "",
"license": "ISC", "license": "ISC",
"description": "", "description": "",
"dependencies": { "dependencies": {
"@modelcontextprotocol/sdk": "^1.0.4",
"@openrouter/sdk": "^0.11.2", "@openrouter/sdk": "^0.11.2",
"chalk": "^5.6.2", "chalk": "^5.6.2",
"dotenv": "^17.2.3", "dotenv": "^17.2.3",

View File

@@ -545,7 +545,7 @@
<!-- Example Queries - Moved to top --> <!-- Example Queries - Moved to top -->
<div class="example-queries" style="margin-top: 0; padding-top: 0; border-top: none; margin-bottom: 20px;"> <div class="example-queries" style="margin-top: 0; padding-top: 0; border-top: none; margin-bottom: 20px;">
<h3>Beispiel Anfragen:</h3> <h3>Beispiel Anfragen:</h3>
<button class="example-btn" onclick="setExample('Wie ist die Lage im Iran?')">Lage im Iran</button> <button class="example-btn" onclick="setExample('Wie ist die aktuelle Nachrichten Lage im Iran?')">Lage im Iran</button>
<button class="example-btn" onclick="setExample('Welche KI Modelle wurden in den letzten Tagen veröffentlicht?')">Neue KI-Modelle</button> <button class="example-btn" onclick="setExample('Welche KI Modelle wurden in den letzten Tagen veröffentlicht?')">Neue KI-Modelle</button>
<button class="example-btn" onclick="setExample('Wie ist das Wetter in Dresden?')">Wetter in Dresden</button> <button class="example-btn" onclick="setExample('Wie ist das Wetter in Dresden?')">Wetter in Dresden</button>
<button class="example-btn" onclick="setExample('Was ist neu in React 19.2?')">React 19.2</button> <button class="example-btn" onclick="setExample('Was ist neu in React 19.2?')">React 19.2</button>

124
src/mcpServer.js Normal file
View File

@@ -0,0 +1,124 @@
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import {
CallToolRequestSchema,
ListToolsRequestSchema,
} from '@modelcontextprotocol/sdk/types.js';
import { createClients } from './clients.js';
import { getConfig, validateConfig } from './config/env.js';
import { createSearchService } from './services/searchService.js';
function createSimpleBroadcast() {
return (message, type = 'info', data = null) => {
// Log to stderr so it doesn't interfere with MCP protocol on stdout
const timestamp = new Date().toLocaleTimeString();
const prefix = type === 'error' ? '❌' : type === 'warning' ? '⚠️' : type === 'success' ? '✅' : '';
console.error(`[${timestamp}] ${prefix} ${message}`);
};
}
export async function startMCPServer() {
const config = getConfig();
validateConfig(config);
const clients = createClients(config);
const broadcast = createSimpleBroadcast();
const searchService = createSearchService({
...clients,
broadcast,
});
const server = new Server(
{
name: 'search-agent',
version: '1.0.0',
},
{
capabilities: {
tools: {},
},
},
);
// Handler for listing available tools
server.setRequestHandler(ListToolsRequestSchema, async () => {
return {
tools: [
{
name: 'search',
description: 'Deep Web Research',
inputSchema: {
type: 'object',
properties: {
question: {
type: 'string',
description: 'The question or topic to search for',
},
previousClarification: {
type: 'string',
description: 'Optional: Previous clarification from the user if the question needed refinement',
},
originalQuestion: {
type: 'string',
description: 'Optional: The original question if this is a follow-up',
},
},
required: ['question'],
},
},
],
};
});
// Handler for calling tools
server.setRequestHandler(CallToolRequestSchema, async (request) => {
const { name, arguments: args } = request.params;
if (name !== 'search') {
throw new Error(`Unknown tool: ${name}`);
}
const { question, previousClarification, originalQuestion } = args;
if (!question) {
throw new Error('Missing required parameter: question');
}
try {
broadcast(`MCP: Starting search for: "${question}"`, 'info');
const result = await searchService.search(question, previousClarification, originalQuestion);
broadcast('MCP: Search completed successfully', 'success');
// Return the result in a structured format
return {
content: [
{
type: 'text',
text: result.fullAnswerHTMLSnippet || 'No answer generated',
},
],
isError: false,
};
} catch (error) {
broadcast(`MCP: Error: ${error.message}`, 'error');
return {
content: [
{
type: 'text',
text: `Error: ${error.message}`,
},
],
isError: true,
};
}
});
// Start the server with stdio transport
const transport = new StdioServerTransport();
await server.connect(transport);
console.error('🔌 MCP Search Agent server running on stdio');
}

View File

@@ -129,7 +129,7 @@ export async function rephraseQuestion({ question, previousClarification, origin
The user has provided this clarification "${previousClarification}" to the original question: ` + originalQuestion; The user has provided this clarification "${previousClarification}" to the original question: ` + originalQuestion;
const params = { const params = {
model: 'openai/gpt-5.4-mini', model: 'z-ai/glm-4.7:nitro',
messages: [ messages: [
{ role: 'system', content: prompt }, { role: 'system', content: prompt },
{ role: 'user', content: question }, { role: 'user', content: question },
@@ -173,7 +173,7 @@ export async function rephraseQuestion({ question, previousClarification, origin
`; `;
const params = { const params = {
model: 'openai/gpt-5.4-mini', model: 'z-ai/glm-4.7:nitro',
messages: [ messages: [
{ role: 'system', content: prompt }, { role: 'system', content: prompt },
{ role: 'user', content: question }, { role: 'user', content: question },
@@ -258,16 +258,14 @@ export async function summarizeFinalAnswer({ openrouter, text, question }) {
const enrichmentPrompt = ` const enrichmentPrompt = `
You are a content formatter. Take the following summary and enrich it with emojis and HTML tags. You are a content formatter. Take the following summary and enrich it with emojis and HTML tags.
Allowed tags: <b>, <i>, <u>, <pre>, <ul>, <li>, <span style="color:...">, <p>, <div>, <hr/> Allowed tags: <b>, <i>, <u>, <pre>, <ul>, <li>, <span style="color:...">, <p>, <div>, <hr/>
Make it visually appealing and easy to read. Keep the same language as the original summary. Make it visually appealing and easy to read.
Summary to enrich:
${summaryData.data.summary}
`; `;
const enrichmentParams = { const enrichmentParams = {
model: 'kwaipilot/kat-coder-pro-v2', model: 'qwen/qwen3-235b-a22b-2507:nitro',
messages: [ messages: [
{ role: 'system', content: enrichmentPrompt }, { role: 'system', content: enrichmentPrompt },
{ role: 'user', content: 'Please enrich the summary with HTML tags and emojis.' }, { role: 'user', content: summaryData.data.summary },
], ],
reasoning: { effort: 'low' }, reasoning: { effort: 'low' },
response_format: { response_format: {
@@ -314,7 +312,7 @@ export async function summarizeFinalAnswer({ openrouter, text, question }) {
}, },
{ {
name: 'enrichment', name: 'enrichment',
model: 'kwaipilot/kat-coder-pro-v2', model: 'qwen/qwen3-235b-a22b-2507:nitro',
cost: enrichmentData.cost, cost: enrichmentData.cost,
promptTokens: enrichmentData.prompt_tokens, promptTokens: enrichmentData.prompt_tokens,
completionTokens: enrichmentData.completion_tokens, completionTokens: enrichmentData.completion_tokens,

View File

@@ -147,7 +147,7 @@ export function createSearchService({ exa, openrouter, broadcast }) {
amount: rephraseResult.cost, amount: rephraseResult.cost,
prompt_tokens: rephraseResult.prompt_tokens, prompt_tokens: rephraseResult.prompt_tokens,
completion_tokens: rephraseResult.completion_tokens, completion_tokens: rephraseResult.completion_tokens,
model: 'openai/gpt-5.4-mini' model: 'z-ai/glm-4.7:nitro'
}); });
} catch (error) { } catch (error) {
throw new SearchServiceError('Failed to generate summary', 500, error); throw new SearchServiceError('Failed to generate summary', 500, error);