Skip to content

Commit b5b6e1d

Browse files
add mcp for web (#6411)
* add mcp for web * update /jan/v1 endpoint to /v1 * update mise and makefile * update yarn lock * use mcp oauth properly
1 parent 7212811 commit b5b6e1d

File tree

30 files changed

+2670
-74
lines changed

30 files changed

+2670
-74
lines changed

Makefile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,9 +40,11 @@ install-web-app: config-yarn
4040
yarn install
4141

4242
dev-web-app: install-web-app
43+
yarn build:core
4344
yarn dev:web-app
4445

4546
build-web-app: install-web-app
47+
yarn build:core
4648
yarn build:web-app
4749

4850
serve-web-app:

core/src/browser/extension.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ export enum ExtensionTypeEnum {
77
Inference = 'inference',
88
Model = 'model',
99
SystemMonitoring = 'systemMonitoring',
10+
MCP = 'mcp',
1011
HuggingFace = 'huggingFace',
1112
Engine = 'engine',
1213
Hardware = 'hardware',

core/src/browser/extensions/index.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,11 @@ export { InferenceExtension } from './inference'
1414
*/
1515
export { AssistantExtension } from './assistant'
1616

17+
/**
18+
* MCP extension for managing tools and server communication.
19+
*/
20+
export { MCPExtension } from './mcp'
21+
1722
/**
1823
* Base AI Engines.
1924
*/
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
import { describe, it, expect, beforeEach } from 'vitest'
2+
import { MCPExtension } from './mcp'
3+
import { ExtensionTypeEnum } from '../extension'
4+
import { MCPTool, MCPToolCallResult } from '../../types'
5+
6+
class TestMCPExtension extends MCPExtension {
7+
constructor() {
8+
super('test://mcp', 'test-mcp')
9+
}
10+
11+
async getTools(): Promise<MCPTool[]> {
12+
return [
13+
{
14+
name: 'test_tool',
15+
description: 'A test tool',
16+
inputSchema: { type: 'object' },
17+
server: 'test-server'
18+
}
19+
]
20+
}
21+
22+
async callTool(toolName: string, args: Record<string, unknown>): Promise<MCPToolCallResult> {
23+
return {
24+
error: '',
25+
content: [{ type: 'text', text: `Called ${toolName} with ${JSON.stringify(args)}` }]
26+
}
27+
}
28+
29+
async getConnectedServers(): Promise<string[]> {
30+
return ['test-server']
31+
}
32+
33+
async refreshTools(): Promise<void> {
34+
// Mock implementation
35+
}
36+
37+
async isHealthy(): Promise<boolean> {
38+
return true
39+
}
40+
41+
async onLoad(): Promise<void> {
42+
// Mock implementation
43+
}
44+
45+
onUnload(): void {
46+
// Mock implementation
47+
}
48+
}
49+
50+
describe('MCPExtension', () => {
51+
let mcpExtension: TestMCPExtension
52+
53+
beforeEach(() => {
54+
mcpExtension = new TestMCPExtension()
55+
})
56+
57+
describe('type', () => {
58+
it('should return MCP extension type', () => {
59+
expect(mcpExtension.type()).toBe(ExtensionTypeEnum.MCP)
60+
})
61+
})
62+
63+
describe('getTools', () => {
64+
it('should return array of MCP tools', async () => {
65+
const tools = await mcpExtension.getTools()
66+
expect(tools).toHaveLength(1)
67+
expect(tools[0]).toEqual({
68+
name: 'test_tool',
69+
description: 'A test tool',
70+
inputSchema: { type: 'object' },
71+
server: 'test-server'
72+
})
73+
})
74+
})
75+
76+
describe('callTool', () => {
77+
it('should call tool and return result', async () => {
78+
const result = await mcpExtension.callTool('test_tool', { param: 'value' })
79+
expect(result).toEqual({
80+
error: '',
81+
content: [{ type: 'text', text: 'Called test_tool with {"param":"value"}' }]
82+
})
83+
})
84+
})
85+
86+
describe('getConnectedServers', () => {
87+
it('should return list of connected servers', async () => {
88+
const servers = await mcpExtension.getConnectedServers()
89+
expect(servers).toEqual(['test-server'])
90+
})
91+
})
92+
93+
describe('isHealthy', () => {
94+
it('should return health status', async () => {
95+
const healthy = await mcpExtension.isHealthy()
96+
expect(healthy).toBe(true)
97+
})
98+
})
99+
})

core/src/browser/extensions/mcp.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import { MCPInterface, MCPTool, MCPToolCallResult } from '../../types'
2+
import { BaseExtension, ExtensionTypeEnum } from '../extension'
3+
4+
/**
5+
* MCP (Model Context Protocol) extension for managing tools and server communication.
6+
* @extends BaseExtension
7+
*/
8+
export abstract class MCPExtension extends BaseExtension implements MCPInterface {
9+
/**
10+
* MCP extension type.
11+
*/
12+
type(): ExtensionTypeEnum | undefined {
13+
return ExtensionTypeEnum.MCP
14+
}
15+
16+
abstract getTools(): Promise<MCPTool[]>
17+
abstract callTool(toolName: string, args: Record<string, unknown>): Promise<MCPToolCallResult>
18+
abstract getConnectedServers(): Promise<string[]>
19+
abstract refreshTools(): Promise<void>
20+
abstract isHealthy(): Promise<boolean>
21+
}

core/src/types/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,4 @@ export * from './api'
1010
export * from './setting'
1111
export * from './engine'
1212
export * from './hardware'
13+
export * from './mcp'

core/src/types/mcp/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
export * from './mcpEntity'
2+
export * from './mcpInterface'

core/src/types/mcp/mcpEntity.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
/**
2+
* MCP (Model Context Protocol) entities
3+
*/
4+
5+
export interface MCPTool {
6+
name: string
7+
description: string
8+
inputSchema: Record<string, unknown>
9+
server: string
10+
}
11+
12+
export interface MCPToolCallResult {
13+
error: string
14+
content: Array<{
15+
type?: string
16+
text: string
17+
}>
18+
}
19+
20+
export interface MCPServerInfo {
21+
name: string
22+
connected: boolean
23+
tools?: MCPTool[]
24+
}

core/src/types/mcp/mcpInterface.ts

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
/**
2+
* MCP (Model Context Protocol) interface
3+
*/
4+
5+
import { MCPTool, MCPToolCallResult } from './mcpEntity'
6+
7+
export interface MCPInterface {
8+
/**
9+
* Get all available MCP tools
10+
*/
11+
getTools(): Promise<MCPTool[]>
12+
13+
/**
14+
* Call a specific MCP tool
15+
*/
16+
callTool(toolName: string, args: Record<string, unknown>): Promise<MCPToolCallResult>
17+
18+
/**
19+
* Get list of connected MCP servers
20+
*/
21+
getConnectedServers(): Promise<string[]>
22+
23+
/**
24+
* Refresh the list of available tools
25+
*/
26+
refreshTools(): Promise<void>
27+
28+
/**
29+
* Check if MCP service is healthy
30+
*/
31+
isHealthy(): Promise<boolean>
32+
}

extensions-web/package.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,5 +30,8 @@
3030
"peerDependencies": {
3131
"@janhq/core": "*",
3232
"zustand": "^5.0.0"
33+
},
34+
"dependencies": {
35+
"@modelcontextprotocol/sdk": "^1.17.5"
3336
}
3437
}

0 commit comments

Comments
 (0)