Skip to content

Commit c978a88

Browse files
arre-ankitmsaaddev
andauthored
📦 NEW: Docs MCP Server (#96)
* 📦 NEW: Docs MCP Server * 👌 IMPROVE: README.md updated * 🐛 FIX: formating * 👌 IMPROVE: Review by SI * 🐛 FIX: utils functions * 👌 IMPROVE: Review by SI --------- Co-authored-by: msaaddev <[email protected]>
1 parent dc1c367 commit c978a88

File tree

9 files changed

+774
-6
lines changed

9 files changed

+774
-6
lines changed

packages/cli/README.md

Lines changed: 60 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,63 @@
1-
## Langbase CLI
1+
# Langbase CLI
22

3-
Langbase CLI is a command line interface for Langbase. It allows you to interact with Langbase from the command line.
3+
The Langbase CLI is a command-line interface for Langbase. It provides a set of commands to interact with the Langbase SDK and perform various tasks related to AI development.
44

5-
## Documentation
5+
## Usage
66

7-
Please follow the [Langbase documentation](https://langbase.com/docs) for the latest information.
7+
### Langbase Docs MCP Server
8+
9+
Integrate the Langbase Docs MCP server into your IDEs and Claude Desktop.
10+
11+
#### Cursor
12+
- Open Cursor settings
13+
- Navigate to the MCP settings
14+
- Click on the `+` button to add a new global MCP server
15+
- Paste the following configuration in the `mcp.json` file:
16+
```json
17+
{
18+
"mcpServers": {
19+
"Langbase": {
20+
"command": "npx",
21+
"args": ["@langbase/cli","docs-mcp-server"]
22+
}
23+
}
24+
}
25+
```
26+
27+
#### Windsurf
28+
- Navigate to Windsurf - Settings > Advanced Settings
29+
- You will find the option to Add Server
30+
- Click “Add custom server +”
31+
- Paste the following configuration in the `mcp_config.json` file:
32+
```json
33+
{
34+
"mcpServers": {
35+
"Langbase": {
36+
"command": "npx",
37+
"args": ["@langbase/cli", "docs-mcp-server"]
38+
}
39+
}
40+
}
41+
```
42+
43+
#### Claude Desktop
44+
- Open Claude Desktop File Menu
45+
- Navigate to the settings
46+
- Go to Developer Tab
47+
- Click on the Edit Config button
48+
- Paste the following configuration in the `claude_desktop_config.json` file:
49+
```json
50+
{
51+
"mcpServers": {
52+
"Langbase": {
53+
"command": "npx",
54+
"args": ["@langbase/cli", "docs-mcp-server"]
55+
}
56+
}
57+
}
58+
```
59+
60+
## Next steps
61+
62+
- Read the [Langbase SDK documentation](https://langbase.com/docs/sdk) for more details
63+
- Join our [Discord](https://langbase.com/discord) community for feedback, requests, and support

packages/cli/package.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@
5151
"@clack/prompts": "^0.7.0",
5252
"@hono/node-server": "^1.13.1",
5353
"@hono/zod-openapi": "^0.16.0",
54+
"@modelcontextprotocol/sdk": "^1.8.0",
5455
"@sindresorhus/slugify": "^2.2.1",
5556
"camelcase": "^8.0.0",
5657
"chalk": "^5.3.0",
@@ -71,6 +72,7 @@
7172
"get-package-json-file": "^2.0.0",
7273
"hono": "^4.5.11",
7374
"js-tiktoken": "^1.0.14",
75+
"jsdom": "^24.1.0",
7476
"log-symbols": "^7.0.0",
7577
"lowdb": "^7.0.1",
7678
"meow": "^13.2.0",
@@ -90,6 +92,7 @@
9092
"devDependencies": {
9193
"@langbase/eslint-config": "workspace:*",
9294
"@langbase/tsconfig": "workspace:*",
95+
"@types/jsdom": "^21.1.7",
9396
"@types/node": "^22.6.1",
9497
"tsup": "^8.3.0",
9598
"tsx": "^4.19.1",
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
import { JSDOM } from 'jsdom';
2+
3+
/**
4+
* Fetches a list of all the docs on the langbase website
5+
*
6+
*
7+
* @returns {Promise<string>} A promise that resolves to a string of all the docs on the langbase website
8+
*/
9+
export async function fetchDocsList() {
10+
try {
11+
const response = await fetch('https://langbase.com/docs/llms.txt');
12+
if (!response.ok) {
13+
throw new Error('Failed to fetch docs');
14+
}
15+
16+
const text = await response.text();
17+
return text;
18+
} catch (error) {
19+
throw new Error('Failed to fetch docs ' + JSON.stringify(error));
20+
}
21+
}
22+
23+
/**
24+
* Fetches and converts a blog post to markdown
25+
*
26+
*
27+
* @param {string} url - The URL of the blog post to fetch
28+
* @returns {Promise<string>} A promise that resolves to a string of the blog post in markdown format
29+
*/
30+
export async function fetchDocsPost(url: string): Promise<string> {
31+
try {
32+
const response = await fetch(url);
33+
if (!response.ok) {
34+
throw new Error('Failed to fetch blog post');
35+
}
36+
37+
const html = await response.text();
38+
39+
const dom = new JSDOM(html);
40+
const document = dom.window.document;
41+
42+
// Remove Next.js initialization code
43+
const scripts = document.querySelectorAll('script');
44+
scripts.forEach(script => script.remove());
45+
46+
// Get the main content
47+
const content = document.body.textContent?.trim() || '';
48+
if (!content) {
49+
throw new Error('No content found in docs');
50+
}
51+
52+
return content;
53+
} catch (error) {
54+
throw new Error(
55+
`Failed to fetch docs: ${error instanceof Error ? error.message : 'Something went wrong. Please try again.'}`
56+
);
57+
}
58+
}
Lines changed: 181 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,181 @@
1+
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2+
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
3+
import { z } from 'zod';
4+
import { fetchDocsList, fetchDocsPost } from './docs';
5+
import { getRelevanceScore } from '@/utils/get-score';
6+
7+
export async function docsMcpServer() {
8+
const server = new McpServer({
9+
name: 'langbase-docs-server',
10+
version: '0.1.0'
11+
});
12+
13+
server.tool(
14+
'docs-route-finder',
15+
"Searches through all available documentation routes and returns relevant paths based on the user's query. This tool helps navigate the documentation by finding the most appropriate sections that match the search criteria.",
16+
{
17+
query: z.string()
18+
.describe(`A refined search term extracted from the user's question.
19+
For example, if user asks 'How do I create a pipe?', the query would be 'SDK Pipe'.
20+
This should be the specific concept or topic to search for in the documentation.
21+
Treat keyword add as create if user ask for Eg. 'How do I add memory to pipe?' the query should be 'create memory'`)
22+
},
23+
async ({ query }) => {
24+
const docs = await fetchDocsList();
25+
// search through the docs and return the most relevent path based on the query
26+
const docLines = docs.split('\n').filter(line => line.trim());
27+
28+
// Score and sort the documentation entries
29+
const scoredDocs = docLines
30+
.map(line => ({
31+
line,
32+
score: getRelevanceScore(line, query)
33+
}))
34+
.sort((a, b) => b.score - a.score)
35+
.filter(doc => doc.score > 0)
36+
.slice(0, 3); // Get top 3 most relevant results
37+
38+
const hasRelevantDocs = scoredDocs.length === 0;
39+
40+
if (hasRelevantDocs) {
41+
return {
42+
content: [
43+
{
44+
type: 'text',
45+
text:
46+
'No relevant documentation found for the query: ' +
47+
query
48+
}
49+
]
50+
};
51+
}
52+
53+
const results = scoredDocs.map(doc => doc.line).join('\n');
54+
55+
return {
56+
content: [
57+
{
58+
type: 'text',
59+
text: results
60+
}
61+
]
62+
};
63+
}
64+
);
65+
66+
server.tool(
67+
'sdk-documentation-fetcher',
68+
'Fetches detailed SDK documentation, specializing in implementation guides for core features like pipes, memory, and tools. This is the primary source for the latest SDK documentation and should be consulted first for questions about creating or implementing SDK components. Use this tool for detailed step-by-step instructions on building pipes, configuring memory systems, and developing custom tools.',
69+
{
70+
url: z
71+
.string()
72+
.describe(
73+
'URL of a specific SDK page to fetch. Format: /sdk/...'
74+
)
75+
},
76+
async ({ url }) => {
77+
const content = await fetchDocsPost(
78+
`https://langbase.com/docs${url}`
79+
);
80+
return {
81+
content: [
82+
{
83+
type: 'text',
84+
text: content
85+
}
86+
]
87+
};
88+
}
89+
);
90+
91+
server.tool(
92+
'examples-tool',
93+
'Fetches code examples and sample implementations from the documentation. Use this tool when users specifically request examples, sample code, or implementation demonstrations. This tool provides practical code snippets and complete working examples that demonstrate how to implement various features.',
94+
{
95+
url: z
96+
.string()
97+
.describe(
98+
'URL of a specific examples page to fetch. Format: /examples/...'
99+
)
100+
},
101+
async ({ url }) => {
102+
const content = await fetchDocsPost(
103+
`https://langbase.com/docs${url}`
104+
);
105+
return {
106+
content: [
107+
{
108+
type: 'text',
109+
text: content
110+
}
111+
]
112+
};
113+
}
114+
);
115+
116+
server.tool(
117+
'guide-tool',
118+
'Fetches detailed guides and tutorials from the documentation. Use this tool when users explicitly request guides, tutorials, or how-to content. This tool provides step-by-step instructions and practical examples for implementing various features.',
119+
{
120+
url: z
121+
.string()
122+
.describe(
123+
'URL of a specific guide page to fetch. Format: /guides/...'
124+
)
125+
},
126+
async ({ url }) => {
127+
const content = await fetchDocsPost(
128+
`https://langbase.com/docs${url}`
129+
);
130+
return {
131+
content: [
132+
{
133+
type: 'text',
134+
text: content
135+
}
136+
]
137+
};
138+
}
139+
);
140+
141+
server.tool(
142+
'api-reference-tool',
143+
'Fetches API reference documentation. Use this tool ONLY when the user explicitly asks about API endpoints, REST API calls, or programmatically creating/updating/deleting resources (like pipes, memory, etc.) through the API interface. For general SDK implementation questions, use the sdk-documentation-fetcher instead.',
144+
{
145+
url: z
146+
.string()
147+
.describe(
148+
'URL of a specific api-reference page to fetch. Format: /api-reference/...'
149+
)
150+
},
151+
async ({ url }) => {
152+
const content = await fetchDocsPost(
153+
`https://langbase.com/docs${url}`
154+
);
155+
return {
156+
content: [
157+
{
158+
type: 'text',
159+
text: content
160+
}
161+
]
162+
};
163+
}
164+
);
165+
166+
async function main() {
167+
const transport = new StdioServerTransport();
168+
169+
try {
170+
await server.connect(transport);
171+
} catch (error) {
172+
console.error('Error connecting to transport:', error);
173+
process.exit(1);
174+
}
175+
}
176+
177+
main().catch(error => {
178+
console.error('Something went wrong:', error);
179+
process.exit(1);
180+
});
181+
}

packages/cli/src/index.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
import { auth } from './auth';
33
import { build } from './build';
44
import { deploy } from './deploy';
5+
import { docsMcpServer } from './docs-mcp-server';
56
import cli from './utils/cli';
67
import debugMode from './utils/debug-mode';
78
import cliInit from './utils/init';
@@ -16,7 +17,10 @@ const command = (cmd: string): boolean => input.includes(cmd);
1617
const flag = (flg: string): boolean => Boolean(flags[flg]);
1718

1819
(async () => {
19-
await cliInit({ clear });
20+
// Skip welcome message for docs-mcp-server command
21+
if (!command('docs-mcp-server')) {
22+
await cliInit({ clear });
23+
}
2024
if (debug) debugMode(cli);
2125

2226
if (command('help')) {
@@ -42,4 +46,8 @@ const flag = (flg: string): boolean => Boolean(flags[flg]);
4246

4347
await deploy({ isDev, agent, filePath, apiKey });
4448
}
49+
50+
if (command('docs-mcp-server')) {
51+
await docsMcpServer();
52+
}
4553
})();

packages/cli/src/utils/cli.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import meowHelp from 'cli-meow-help';
2+
// @ts-ignore
23
import meow from 'meow';
34

45
const flags = {
@@ -37,7 +38,7 @@ const commands = {
3738
};
3839

3940
const helpText = meowHelp({
40-
name: `baseai`,
41+
name: `@langbase/cli`,
4142
flags,
4243
commands,
4344
desc: false,

0 commit comments

Comments
 (0)