♻️ refactor: change the klavis github tools into lobehub skill & add vercel skills (#13442)

* refactor: change the klavis github tools into lobehub skill & add the vercel skill

* fix: slove the test & topicid parse
This commit is contained in:
LiJian 2026-04-01 14:48:16 +08:00 committed by GitHub
parent fee0fe5699
commit 19f90e3d9a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
13 changed files with 57 additions and 26 deletions

View file

@ -266,7 +266,7 @@
"@lobehub/desktop-ipc-typings": "workspace:*",
"@lobehub/editor": "^4.5.0",
"@lobehub/icons": "^5.0.0",
"@lobehub/market-sdk": "0.31.11",
"@lobehub/market-sdk": "0.32.2",
"@lobehub/tts": "^5.1.2",
"@lobehub/ui": "^5.6.1",
"@modelcontextprotocol/sdk": "^1.26.0",

View file

@ -117,7 +117,7 @@ export class CredsExecutionRuntime {
const providerConfig = getLobehubSkillProviderById(provider);
if (!providerConfig) {
return {
content: `Unknown OAuth provider: ${provider}. Available providers: github, linear, microsoft, twitter`,
content: `Unknown OAuth provider: ${provider}. Available providers: github, linear, microsoft, twitter, vercel`,
error: {
message: `Unknown OAuth provider: ${provider}`,
type: 'UnknownProvider',

View file

@ -1,5 +1,5 @@
import type { IconType } from '@icons-pack/react-simple-icons';
import { SiCaldotcom, SiGithub } from '@icons-pack/react-simple-icons';
import { SiCaldotcom } from '@icons-pack/react-simple-icons';
import { Klavis } from 'klavis';
export interface KlavisServerType {
@ -96,17 +96,6 @@ export const KLAVIS_SERVER_TYPES: KlavisServerType[] = [
label: 'Google Docs',
serverName: Klavis.McpServerName.GoogleDocs,
},
{
author: 'Klavis',
authorUrl: 'https://klavis.io',
description: 'Enhanced GitHub MCP Server',
icon: SiGithub,
identifier: 'github',
readme:
'Connect to GitHub to manage repositories, issues, pull requests, and code. Search code, review changes, create branches, and collaborate on software development projects through conversational AI.',
label: 'GitHub',
serverName: Klavis.McpServerName.Github,
},
{
author: 'Klavis',
authorUrl: 'https://klavis.io',

View file

@ -1,5 +1,5 @@
import type { IconType } from '@icons-pack/react-simple-icons';
import { SiGithub, SiLinear, SiX } from '@icons-pack/react-simple-icons';
import { SiGithub, SiLinear, SiVercel, SiX } from '@icons-pack/react-simple-icons';
export interface LobehubSkillProviderType {
/**
@ -93,6 +93,18 @@ export const LOBEHUB_SKILL_PROVIDERS: LobehubSkillProviderType[] = [
'Connect to X (Twitter) to post tweets, manage your timeline, and engage with your audience. Create content, schedule posts, monitor mentions, and build your social media presence through conversational AI.',
label: 'X (Twitter)',
},
{
author: 'LobeHub',
authorUrl: 'https://lobehub.com',
defaultVisible: true,
description:
'Vercel is a cloud platform for frontend developers, providing hosting and serverless functions to deploy web applications with ease.',
icon: SiVercel,
id: 'vercel',
readme:
'Connect to Vercel to manage your deployments, monitor project status, and control your infrastructure. Deploy applications, check build logs, manage environment variables, and scale your projects through conversational AI.',
label: 'Vercel',
},
];
/**

View file

@ -8,7 +8,7 @@
"@lobechat/python-interpreter": "workspace:*",
"@lobechat/web-crawler": "workspace:*",
"@lobehub/chat-plugin-sdk": "^1.32.4",
"@lobehub/market-sdk": "0.31.11",
"@lobehub/market-sdk": "0.32.2",
"@lobehub/market-types": "^1.12.3",
"model-bank": "workspace:*",
"type-fest": "^4.41.0",

View file

@ -398,16 +398,18 @@ export const marketRouter = router({
args: z.record(z.any()).optional(),
provider: z.string(),
toolName: z.string(),
topicId: z.string().optional(),
}),
)
.mutation(async ({ input, ctx }) => {
const { provider, toolName, args } = input;
log('connectCallTool: provider=%s, tool=%s', provider, toolName);
const { provider, toolName, args, topicId } = input;
log('connectCallTool: provider=%s, tool=%s, topicId=%s', provider, toolName, topicId);
try {
const response = await ctx.marketSDK.skills.callTool(provider, {
args: args || {},
tool: toolName,
// @ts-ignore
topicId,
});
log('connectCallTool response: %O', response);

View file

@ -25,6 +25,9 @@ export function extractAccessToken(req: NextRequest): string | undefined {
export interface LobehubSkillExecuteParams {
args: Record<string, any>;
context?: {
topicId?: string;
};
provider: string;
toolName: string;
}
@ -460,13 +463,15 @@ export class MarketService {
* @returns Execution result with content and success status
*/
async executeLobehubSkill(params: LobehubSkillExecuteParams): Promise<LobehubSkillExecuteResult> {
const { provider, toolName, args } = params;
const { provider, toolName, args, context } = params;
log('executeLobehubSkill: %s/%s with args: %O', provider, toolName, args);
log('executeLobehubSkill: %s/%s with args: %O, context: %O', provider, toolName, args, context);
try {
const response = await this.market.skills.callTool(provider, {
args,
// @ts-ignore
topicId: context?.topicId,
tool: toolName,
});

View file

@ -39,6 +39,9 @@ export class BuiltinToolsExecutor implements IToolExecutor {
if (source === 'lobehubSkill') {
return this.marketService.executeLobehubSkill({
args,
context: {
topicId: context.topicId,
},
provider: identifier,
toolName: apiName,
});

View file

@ -4,12 +4,24 @@ import { useToolStore } from '@/store/tool';
import { type ChatToolPayload } from '@/types/message';
import { safeParseJSON } from '@/utils/safeParseJSON';
/**
* Context for remote tool execution, derived from the invoking message
*/
export interface RemoteToolExecutorContext {
/** Topic ID from the message that triggered this tool call */
topicId?: string;
}
/**
* Executor function type for remote tool invocation
* @param payload - Tool call payload
* @param context - Context from the invoking message
* @returns Promise with MCPToolCallResult data
*/
export type RemoteToolExecutor = (payload: ChatToolPayload) => Promise<MCPToolCallResult>;
export type RemoteToolExecutor = (
payload: ChatToolPayload,
context?: RemoteToolExecutorContext,
) => Promise<MCPToolCallResult>;
/**
* Create a failed MCPToolCallResult
@ -23,7 +35,7 @@ const createFailedResult = (
success: false,
});
export const klavisExecutor: RemoteToolExecutor = async (p) => {
export const klavisExecutor: RemoteToolExecutor = async (p, _context) => {
// payload.identifier is now the storage identifier (e.g., 'google-calendar')
const identifier = p.identifier;
const klavisServers = useToolStore.getState().servers || [];
@ -62,7 +74,7 @@ export const klavisExecutor: RemoteToolExecutor = async (p) => {
return createFailedResult('Klavis tool returned empty result');
};
export const lobehubSkillExecutor: RemoteToolExecutor = async (p: any) => {
export const lobehubSkillExecutor: RemoteToolExecutor = async (p, context) => {
// payload.identifier is the provider id (e.g., 'linear', 'microsoft')
const provider = p.identifier;
@ -70,10 +82,12 @@ export const lobehubSkillExecutor: RemoteToolExecutor = async (p: any) => {
const args = safeParseJSON(p.arguments) || {};
// Call LobeHub Skill tool via store action
// topicId comes from message context, not global active state
const result = await useToolStore.getState().callLobehubSkillTool({
args,
provider,
toolName: p.apiName,
topicId: context?.topicId,
});
if (!result.success) {

View file

@ -357,7 +357,9 @@ export class PluginTypesActionImpl {
);
try {
data = await executor(payload);
// Pass topicId from message context, not global active state
// This ensures tool calls use the correct topic even if user switches topics
data = await executor(payload, { topicId: message?.topicId });
} catch (error) {
console.error(`[${logPrefix}] Error:`, error);

View file

@ -71,6 +71,7 @@ describe('lobehubSkillStore actions', () => {
provider: 'linear',
toolName: 'createIssue',
args: { title: 'Test Issue' },
topicId: undefined,
});
});

View file

@ -40,7 +40,7 @@ export class LobehubSkillStoreActionImpl {
callLobehubSkillTool = async (
params: CallLobehubSkillToolParams,
): Promise<CallLobehubSkillToolResult> => {
const { provider, toolName, args } = params;
const { provider, toolName, args, topicId } = params;
const toolId = `${provider}:${toolName}`;
this.#set(
@ -56,6 +56,7 @@ export class LobehubSkillStoreActionImpl {
args,
provider,
toolName,
topicId,
});
this.#set(

View file

@ -83,6 +83,8 @@ export interface CallLobehubSkillToolParams {
provider: string;
/** Tool name */
toolName: string;
/** Topic ID from message context (not global active state) */
topicId?: string;
}
/**