mcp-use 0.0.1
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 +21 -0
- package/README.md +130 -0
- package/dist/index.js +10 -0
- package/dist/src/adapters/base.js +124 -0
- package/dist/src/adapters/index.js +2 -0
- package/dist/src/adapters/langchain_adapter.js +105 -0
- package/dist/src/agents/base.js +9 -0
- package/dist/src/agents/index.js +3 -0
- package/dist/src/agents/mcp_agent.js +324 -0
- package/dist/src/agents/prompts/system_prompt_builder.js +40 -0
- package/dist/src/agents/prompts/templates.js +39 -0
- package/dist/src/agents/server_manager.js +152 -0
- package/dist/src/client.js +124 -0
- package/dist/src/config.js +30 -0
- package/dist/src/connectors/base.js +129 -0
- package/dist/src/connectors/http.js +51 -0
- package/dist/src/connectors/index.js +4 -0
- package/dist/src/connectors/stdio.js +49 -0
- package/dist/src/connectors/websocket.js +151 -0
- package/dist/src/logging.js +59 -0
- package/dist/src/session.js +52 -0
- package/dist/src/task_managers/base.js +127 -0
- package/dist/src/task_managers/index.js +4 -0
- package/dist/src/task_managers/sse.js +44 -0
- package/dist/src/task_managers/stdio.js +52 -0
- package/dist/src/task_managers/websocket.js +67 -0
- package/package.json +75 -0
package/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
MIT License
|
2
|
+
|
3
|
+
Copyright (c) 2025 zane
|
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.
|
package/README.md
ADDED
@@ -0,0 +1,130 @@
|
|
1
|
+
<h1 align="center">Unified MCP Client Library</h1>
|
2
|
+
|
3
|
+
[](https://www.npmjs.com/package/@modelcontextprotocol/mcp-client)
|
4
|
+
[](https://www.npmjs.com/package/@modelcontextprotocol/mcp-client)
|
5
|
+
[](https://github.com/zandko/mcp-use/blob/main/LICENSE)
|
6
|
+
[](https://eslint.org)
|
7
|
+
[](https://github.com/zandko/mcp-use/stargazers)
|
8
|
+
|
9
|
+
🌐 **MCP Client** is the open-source way to connect **any LLM to any MCP server** in TypeScript/Node.js, letting you build custom agents with tool access without closed-source dependencies.
|
10
|
+
|
11
|
+
💡 Let developers easily connect any LLM via LangChain.js to tools like web browsing, file operations, 3D modeling, and more.
|
12
|
+
|
13
|
+
---
|
14
|
+
|
15
|
+
## ✨ Key Features
|
16
|
+
|
17
|
+
| Feature | Description |
|
18
|
+
| ------------------------------- | -------------------------------------------------------------------------- |
|
19
|
+
| 🔄 **Ease of use** | Create an MCP-capable agent in just a few lines of TypeScript. |
|
20
|
+
| 🤖 **LLM Flexibility** | Works with any LangChain.js-supported LLM that supports tool calling. |
|
21
|
+
| 🌐 **HTTP Support** | Direct SSE/HTTP connection to MCP servers. |
|
22
|
+
| ⚙️ **Dynamic Server Selection** | Agents select the right MCP server from a pool on the fly. |
|
23
|
+
| 🧩 **Multi-Server Support** | Use multiple MCP servers in one agent. |
|
24
|
+
| 🛡️ **Tool Restrictions** | Restrict unsafe tools like filesystem or network. |
|
25
|
+
| 🔧 **Custom Agents** | Build your own agents with LangChain.js adapter or implement new adapters. |
|
26
|
+
|
27
|
+
---
|
28
|
+
|
29
|
+
## 🚀 Quick Start
|
30
|
+
|
31
|
+
### Installation
|
32
|
+
|
33
|
+
```bash
|
34
|
+
# Install from npm
|
35
|
+
npm install mcp-use
|
36
|
+
# LangChain.js and your LLM provider (e.g., OpenAI)
|
37
|
+
npm install langchain @langchain/openai dotenv
|
38
|
+
```
|
39
|
+
|
40
|
+
Create a `.env`:
|
41
|
+
|
42
|
+
```ini
|
43
|
+
OPENAI_API_KEY=your_api_key
|
44
|
+
```
|
45
|
+
|
46
|
+
### Basic Usage
|
47
|
+
|
48
|
+
```ts
|
49
|
+
import { ChatOpenAI } from '@langchain/openai'
|
50
|
+
import { MCPAgent, MCPClient } from 'mcp-use'
|
51
|
+
import 'dotenv/config'
|
52
|
+
|
53
|
+
async function main() {
|
54
|
+
// 1. Configure MCP servers
|
55
|
+
const config = {
|
56
|
+
mcpServers: {
|
57
|
+
playwright: { command: 'npx', args: ['@playwright/mcp@latest'] }
|
58
|
+
}
|
59
|
+
}
|
60
|
+
const client = MCPClient.fromDict(config)
|
61
|
+
|
62
|
+
// 2. Create LLM
|
63
|
+
const llm = new ChatOpenAI({ modelName: 'gpt-4o' })
|
64
|
+
|
65
|
+
// 3. Instantiate agent
|
66
|
+
const agent = new MCPAgent({ llm, client, maxSteps: 20 })
|
67
|
+
|
68
|
+
// 4. Run query
|
69
|
+
const result = await agent.run('Find the best restaurant in Tokyo using Google Search')
|
70
|
+
console.log('Result:', result)
|
71
|
+
}
|
72
|
+
|
73
|
+
main().catch(console.error)
|
74
|
+
```
|
75
|
+
|
76
|
+
---
|
77
|
+
|
78
|
+
## 📂 Configuration File
|
79
|
+
|
80
|
+
You can store servers in a JSON file:
|
81
|
+
|
82
|
+
```json
|
83
|
+
{
|
84
|
+
"mcpServers": {
|
85
|
+
"playwright": {
|
86
|
+
"command": "npx",
|
87
|
+
"args": ["@playwright/mcp@latest"]
|
88
|
+
}
|
89
|
+
}
|
90
|
+
}
|
91
|
+
```
|
92
|
+
|
93
|
+
Load it:
|
94
|
+
|
95
|
+
```ts
|
96
|
+
import { MCPClient } from 'mcp-use'
|
97
|
+
const client = MCPClient.fromConfigFile('./mcp-config.json')
|
98
|
+
```
|
99
|
+
|
100
|
+
---
|
101
|
+
|
102
|
+
## 🔄 Multi-Server Example
|
103
|
+
|
104
|
+
```ts
|
105
|
+
const config = {
|
106
|
+
mcpServers: {
|
107
|
+
airbnb: { command: 'npx', args: ['@openbnb/mcp-server-airbnb'] },
|
108
|
+
playwright: { command: 'npx', args: ['@playwright/mcp@latest'] }
|
109
|
+
}
|
110
|
+
}
|
111
|
+
const client = MCPClient.fromDict(config)
|
112
|
+
const agent = new MCPAgent({ llm, client, useServerManager: true })
|
113
|
+
await agent.run('Search Airbnb in Barcelona, then Google restaurants nearby')
|
114
|
+
```
|
115
|
+
|
116
|
+
---
|
117
|
+
|
118
|
+
## 🔒 Tool Access Control
|
119
|
+
|
120
|
+
```ts
|
121
|
+
const agent = new MCPAgent({
|
122
|
+
llm,
|
123
|
+
client,
|
124
|
+
disallowedTools: ['file_system', 'network']
|
125
|
+
})
|
126
|
+
```
|
127
|
+
|
128
|
+
## 📜 License
|
129
|
+
|
130
|
+
MIT © [Zane](https://github.com/zandko)
|
package/dist/index.js
ADDED
@@ -0,0 +1,10 @@
|
|
1
|
+
import { MCPAgent } from './src/agents/mcp_agent.js';
|
2
|
+
import { MCPClient } from './src/client.js';
|
3
|
+
import { loadConfigFile } from './src/config.js';
|
4
|
+
import { BaseConnector } from './src/connectors/base.js';
|
5
|
+
import { HttpConnector } from './src/connectors/http.js';
|
6
|
+
import { StdioConnector } from './src/connectors/stdio.js';
|
7
|
+
import { WebSocketConnector } from './src/connectors/websocket.js';
|
8
|
+
import { Logger, logger } from './src/logging.js';
|
9
|
+
import { MCPSession } from './src/session.js';
|
10
|
+
export { BaseConnector, HttpConnector, loadConfigFile, Logger, logger, MCPAgent, MCPClient, MCPSession, StdioConnector, WebSocketConnector };
|
@@ -0,0 +1,124 @@
|
|
1
|
+
import { logger } from '../logging.js';
|
2
|
+
/**
|
3
|
+
* Abstract base class for converting MCP tools to other framework formats.
|
4
|
+
*
|
5
|
+
* This class defines the common interface that all adapter implementations
|
6
|
+
* should follow to ensure consistency across different frameworks.
|
7
|
+
*/
|
8
|
+
export class BaseAdapter {
|
9
|
+
/**
|
10
|
+
* List of tool names that should not be available.
|
11
|
+
*/
|
12
|
+
disallowedTools;
|
13
|
+
/**
|
14
|
+
* Internal cache that maps a connector instance to the list of tools
|
15
|
+
* generated for it.
|
16
|
+
*/
|
17
|
+
connectorToolMap = new Map();
|
18
|
+
constructor(disallowedTools) {
|
19
|
+
this.disallowedTools = disallowedTools ?? [];
|
20
|
+
}
|
21
|
+
/**
|
22
|
+
* Create tools from an MCPClient instance.
|
23
|
+
*
|
24
|
+
* This is the recommended way to create tools from an MCPClient, as it handles
|
25
|
+
* session creation and connector extraction automatically.
|
26
|
+
*
|
27
|
+
* @param client The MCPClient to extract tools from.
|
28
|
+
* @param disallowedTools Optional list of tool names to exclude.
|
29
|
+
* @returns A promise that resolves with a list of converted tools.
|
30
|
+
*/
|
31
|
+
static async createTools(client, disallowedTools) {
|
32
|
+
// Create the adapter
|
33
|
+
const adapter = new this(disallowedTools);
|
34
|
+
// Ensure we have active sessions
|
35
|
+
if (!client.activeSessions || Object.keys(client.activeSessions).length === 0) {
|
36
|
+
logger.info('No active sessions found, creating new ones...');
|
37
|
+
await client.createAllSessions();
|
38
|
+
}
|
39
|
+
// Get all active sessions
|
40
|
+
const sessions = client.getAllActiveSessions();
|
41
|
+
// Extract connectors from sessions
|
42
|
+
const connectors = Object.values(sessions).map(session => session.connector);
|
43
|
+
// Create tools from connectors
|
44
|
+
return adapter.createToolsFromConnectors(connectors);
|
45
|
+
}
|
46
|
+
/**
|
47
|
+
* Dynamically load tools for a specific connector.
|
48
|
+
*
|
49
|
+
* @param connector The connector to load tools for.
|
50
|
+
* @returns The list of tools that were loaded in the target framework's format.
|
51
|
+
*/
|
52
|
+
async loadToolsForConnector(connector) {
|
53
|
+
// Return cached tools if we already processed this connector
|
54
|
+
if (this.connectorToolMap.has(connector)) {
|
55
|
+
const cached = this.connectorToolMap.get(connector);
|
56
|
+
logger.debug(`Returning ${cached.length} existing tools for connector`);
|
57
|
+
return cached;
|
58
|
+
}
|
59
|
+
const connectorTools = [];
|
60
|
+
// Make sure the connector is initialized and has tools
|
61
|
+
const success = await this.ensureConnectorInitialized(connector);
|
62
|
+
if (!success) {
|
63
|
+
return [];
|
64
|
+
}
|
65
|
+
// Convert and collect tools
|
66
|
+
for (const tool of connector.tools) {
|
67
|
+
const converted = this.convertTool(tool, connector);
|
68
|
+
if (converted) {
|
69
|
+
connectorTools.push(converted);
|
70
|
+
}
|
71
|
+
}
|
72
|
+
// Cache the tools for this connector
|
73
|
+
this.connectorToolMap.set(connector, connectorTools);
|
74
|
+
// Log for debugging purposes
|
75
|
+
logger.debug(`Loaded ${connectorTools.length} new tools for connector: ${connectorTools
|
76
|
+
.map((t) => t?.name ?? String(t))
|
77
|
+
.join(', ')}`);
|
78
|
+
return connectorTools;
|
79
|
+
}
|
80
|
+
/**
|
81
|
+
* Create tools from MCP tools in all provided connectors.
|
82
|
+
*
|
83
|
+
* @param connectors List of MCP connectors to create tools from.
|
84
|
+
* @returns A promise that resolves with all converted tools.
|
85
|
+
*/
|
86
|
+
async createToolsFromConnectors(connectors) {
|
87
|
+
const tools = [];
|
88
|
+
for (const connector of connectors) {
|
89
|
+
const connectorTools = await this.loadToolsForConnector(connector);
|
90
|
+
tools.push(...connectorTools);
|
91
|
+
}
|
92
|
+
logger.debug(`Available tools: ${tools.length}`);
|
93
|
+
return tools;
|
94
|
+
}
|
95
|
+
/**
|
96
|
+
* Check if a connector is initialized and has tools.
|
97
|
+
*
|
98
|
+
* @param connector The connector to check.
|
99
|
+
* @returns True if the connector is initialized and has tools, false otherwise.
|
100
|
+
*/
|
101
|
+
checkConnectorInitialized(connector) {
|
102
|
+
return Boolean(connector.tools && connector.tools.length);
|
103
|
+
}
|
104
|
+
/**
|
105
|
+
* Ensure a connector is initialized.
|
106
|
+
*
|
107
|
+
* @param connector The connector to initialize.
|
108
|
+
* @returns True if initialization succeeded, false otherwise.
|
109
|
+
*/
|
110
|
+
async ensureConnectorInitialized(connector) {
|
111
|
+
if (!this.checkConnectorInitialized(connector)) {
|
112
|
+
logger.debug('Connector doesn\'t have tools, initializing it');
|
113
|
+
try {
|
114
|
+
await connector.initialize();
|
115
|
+
return true;
|
116
|
+
}
|
117
|
+
catch (err) {
|
118
|
+
logger.error(`Error initializing connector: ${err}`);
|
119
|
+
return false;
|
120
|
+
}
|
121
|
+
}
|
122
|
+
return true;
|
123
|
+
}
|
124
|
+
}
|
@@ -0,0 +1,105 @@
|
|
1
|
+
import { JSONSchemaToZod } from '@dmitryrechkin/json-schema-to-zod';
|
2
|
+
import { DynamicStructuredTool } from '@langchain/core/tools';
|
3
|
+
import { z } from 'zod';
|
4
|
+
import { logger } from '../logging.js';
|
5
|
+
import { BaseAdapter } from './base.js';
|
6
|
+
function schemaToZod(schema) {
|
7
|
+
/** Recursively normalise `type: [T, U]` → `anyOf: [{type:T},{type:U}]`. */
|
8
|
+
// const fixSchema = (s: any): any => {
|
9
|
+
// if (s && typeof s === 'object') {
|
10
|
+
// if (Array.isArray(s.type)) {
|
11
|
+
// s.anyOf = s.type.map((t: any) => ({ type: t }))
|
12
|
+
// delete s.type
|
13
|
+
// }
|
14
|
+
// for (const [k, v] of Object.entries(s)) {
|
15
|
+
// s[k] = fixSchema(v)
|
16
|
+
// }
|
17
|
+
// }
|
18
|
+
// return s
|
19
|
+
// }
|
20
|
+
try {
|
21
|
+
// const normalised = fixSchema(structuredClone(schema))
|
22
|
+
const zodSchema = JSONSchemaToZod.convert(schema);
|
23
|
+
return zodSchema;
|
24
|
+
}
|
25
|
+
catch (err) {
|
26
|
+
logger.warn(`Failed to convert JSON schema to Zod: ${err}`);
|
27
|
+
return z.any();
|
28
|
+
}
|
29
|
+
}
|
30
|
+
function parseMcpToolResult(toolResult) {
|
31
|
+
if (toolResult.isError) {
|
32
|
+
throw new Error(`Tool execution failed: ${toolResult.content}`);
|
33
|
+
}
|
34
|
+
if (!toolResult.content || toolResult.content.length === 0) {
|
35
|
+
throw new Error('Tool execution returned no content');
|
36
|
+
}
|
37
|
+
let decoded = '';
|
38
|
+
for (const item of toolResult.content) {
|
39
|
+
switch (item.type) {
|
40
|
+
case 'text': {
|
41
|
+
decoded += item.text;
|
42
|
+
break;
|
43
|
+
}
|
44
|
+
case 'image': {
|
45
|
+
decoded += item.data;
|
46
|
+
break;
|
47
|
+
}
|
48
|
+
case 'resource': {
|
49
|
+
const res = item.resource;
|
50
|
+
if (res?.text !== undefined) {
|
51
|
+
decoded += res.text;
|
52
|
+
}
|
53
|
+
else if (res?.blob !== undefined) {
|
54
|
+
// eslint-disable-next-line node/prefer-global/buffer
|
55
|
+
decoded += res.blob instanceof Uint8Array || res.blob instanceof Buffer
|
56
|
+
// eslint-disable-next-line node/prefer-global/buffer
|
57
|
+
? Buffer.from(res.blob).toString('base64')
|
58
|
+
: String(res.blob);
|
59
|
+
}
|
60
|
+
else {
|
61
|
+
throw new Error(`Unexpected resource type: ${res?.type}`);
|
62
|
+
}
|
63
|
+
break;
|
64
|
+
}
|
65
|
+
default:
|
66
|
+
throw new Error(`Unexpected content type: ${item.type}`);
|
67
|
+
}
|
68
|
+
}
|
69
|
+
return decoded;
|
70
|
+
}
|
71
|
+
export class LangChainAdapter extends BaseAdapter {
|
72
|
+
constructor(disallowedTools = []) {
|
73
|
+
super(disallowedTools);
|
74
|
+
}
|
75
|
+
/**
|
76
|
+
* Convert a single MCP tool specification into a LangChainJS structured tool.
|
77
|
+
*/
|
78
|
+
convertTool(mcpTool, connector) {
|
79
|
+
// Filter out disallowed tools early.
|
80
|
+
if (this.disallowedTools.includes(mcpTool.name)) {
|
81
|
+
return null;
|
82
|
+
}
|
83
|
+
// Derive a strict Zod schema for the tool's arguments.
|
84
|
+
const argsSchema = mcpTool.inputSchema
|
85
|
+
? schemaToZod(mcpTool.inputSchema)
|
86
|
+
: z.object({}).optional();
|
87
|
+
const tool = new DynamicStructuredTool({
|
88
|
+
name: mcpTool.name ?? 'NO NAME',
|
89
|
+
description: mcpTool.description ?? '', // Blank is acceptable but discouraged.
|
90
|
+
schema: argsSchema,
|
91
|
+
func: async (input) => {
|
92
|
+
logger.debug(`MCP tool \"${mcpTool.name}\" received input: ${JSON.stringify(input)}`);
|
93
|
+
try {
|
94
|
+
const result = await connector.callTool(mcpTool.name, input);
|
95
|
+
return parseMcpToolResult(result);
|
96
|
+
}
|
97
|
+
catch (err) {
|
98
|
+
logger.error(`Error executing MCP tool: ${err}`);
|
99
|
+
return `Error executing MCP tool: ${String(err)}`;
|
100
|
+
}
|
101
|
+
},
|
102
|
+
});
|
103
|
+
return tool;
|
104
|
+
}
|
105
|
+
}
|