fix: source is not passed to memory tools

This commit is contained in:
Harshith Mullapudi 2025-08-24 19:47:33 +05:30
parent ebf9a0b05e
commit 5d3bdf9bca
6 changed files with 34 additions and 105 deletions

View File

@ -78,7 +78,7 @@ export function McpSourcesStats({
<Card>
<CardHeader>
<CardTitle className="text-lg">Current Active</CardTitle>
<CardTitle className="text-lg">Active</CardTitle>
</CardHeader>
<CardContent className="px-3 pb-4">
{activeSources.length === 0 ? (

View File

@ -1,4 +1,4 @@
import { useEffect, useRef, useState } from "react";
import { useEffect, useRef } from "react";
import {
InfiniteLoader,
AutoSizer,
@ -10,11 +10,7 @@ import {
import { type McpSessionItem } from "~/hooks/use-mcp-sessions";
import { ScrollManagedList } from "../virtualized-list";
import { Badge } from "../ui/badge";
import { Input } from "../ui/input";
import { Button } from "../ui/button";
import { Check, Copy } from "lucide-react";
import { cn } from "~/lib/utils";
import { Card, CardContent } from "../ui/card";
import { getIconForAuthorise } from "../icon-utils";
interface VirtualMcpSessionsListProps {
@ -25,81 +21,6 @@ interface VirtualMcpSessionsListProps {
height?: number;
}
export function MCPUrlBox() {
const [copied, setCopied] = useState(false);
const [selectedSource, setSelectedSource] = useState<
"Claude" | "Cursor" | "Other"
>("Claude");
const getMcpURL = (source: "Claude" | "Cursor" | "Other") => {
const baseUrl = "https://core.heysol.ai/api/v1/mcp";
return `${baseUrl}?source=${source}`;
};
const mcpURL = getMcpURL(selectedSource);
const copyToClipboard = async () => {
try {
await navigator.clipboard.writeText(mcpURL);
setCopied(true);
setTimeout(() => setCopied(false), 2000);
} catch (err) {
console.error("Failed to copy:", err);
}
};
return (
<Card className="min-w-[400px] rounded-lg bg-transparent pt-1">
<CardContent className="pt-2 text-base">
<div className="space-y-4">
<div className="space-y-3">
<div className="bg-grayAlpha-100 flex space-x-1 rounded-lg p-1">
{(["Claude", "Cursor", "Other"] as const).map((source) => (
<Button
key={source}
onClick={() => setSelectedSource(source)}
variant="ghost"
className={cn(
"flex-1 rounded-md px-3 py-1.5 transition-all",
selectedSource === source
? "bg-accent text-accent-foreground shadow-sm"
: "text-muted-foreground hover:text-foreground",
)}
>
{source}
</Button>
))}
</div>
<div className="bg-background-3 flex items-center rounded">
<Input
type="text"
id="mcpURL"
value={mcpURL}
readOnly
className="bg-background-3 block w-full text-base"
/>
<Button
type="button"
variant="link"
size="sm"
onClick={copyToClipboard}
className="px-3"
>
{copied ? (
<Check className="h-4 w-4" />
) : (
<Copy className="h-4 w-4" />
)}
</Button>
</div>
</div>
</div>
</CardContent>
</Card>
);
}
function McpSessionItemRenderer(
props: ListRowProps,
sessions: McpSessionItem[],

View File

@ -1,10 +1,7 @@
import { useState } from "react";
import { useMcpSessions } from "~/hooks/use-mcp-sessions";
import { McpSessionsFilters } from "~/components/mcp/mcp-sessions-filters";
import {
MCPUrlBox,
VirtualMcpSessionsList,
} from "~/components/mcp/virtual-mcp-sessions-list";
import { VirtualMcpSessionsList } from "~/components/mcp/virtual-mcp-sessions-list";
import { McpSourcesStats } from "~/components/mcp/mcp-sources-stats";
import { Card, CardContent } from "~/components/ui/card";
import { Database, LoaderCircle } from "lucide-react";
@ -35,7 +32,6 @@ export default function McpSettings() {
<div className="flex h-[calc(100vh_-_135px)] w-full flex-col items-center space-y-6">
{/* Top Sources Stats */}
<div className="flex w-full flex-col gap-4">
<MCPUrlBox />
<McpSourcesStats
sources={availableSources}
activeSources={activeSources}

View File

@ -13,17 +13,20 @@ import { IntegrationLoader } from "~/utils/mcp/integration-loader";
import { callMemoryTool, memoryTools } from "~/utils/mcp/memory";
import { logger } from "~/services/logger.service";
import { type Response, type Request } from "express";
import { getUser } from "./session.server";
import { getUserById } from "~/models/user.server";
import { getWorkspaceByUser } from "~/models/workspace.server";
const QueryParams = z.object({
source: z.string().optional(),
integrations: z.string().optional(), // comma-separated slugs
no_integrations: z.boolean().optional(), // comma-separated slugs
});
// Create MCP server with memory tools + dynamic integration tools
async function createMcpServer(userId: string, sessionId: string) {
async function createMcpServer(
userId: string,
sessionId: string,
source: string,
) {
const server = new Server(
{
name: "core-unified-mcp-server",
@ -58,7 +61,7 @@ async function createMcpServer(userId: string, sessionId: string) {
// Handle memory tools
if (name.startsWith("memory_")) {
return await callMemoryTool(name, args, userId);
return await callMemoryTool(name, args, userId, source);
}
// Handle integration tools (prefixed with integration slug)
@ -93,6 +96,7 @@ async function createTransport(
sessionId: string,
source: string,
integrations: string[],
noIntegrations: boolean,
userId: string,
workspaceId: string,
): Promise<StreamableHTTPServerTransport> {
@ -136,7 +140,7 @@ async function createTransport(
logger.error("Failed to send keep-alive ping, cleaning up interval." + e);
clearInterval(keepAlive);
}
}, 60000); // Send ping every 60 seconds
}, 30000); // Send ping every 60 seconds
// Setup cleanup on close
transport.onclose = async () => {
@ -147,24 +151,26 @@ async function createTransport(
// Load integration transports
try {
const result = await IntegrationLoader.loadIntegrationTransports(
sessionId,
userId,
workspaceId,
integrations.length > 0 ? integrations : undefined,
);
logger.log(
`Loaded ${result.loaded} integration transports for session ${sessionId}`,
);
if (result.failed.length > 0) {
logger.warn(`Failed to load some integrations: ${result.failed}`);
if (noIntegrations) {
const result = await IntegrationLoader.loadIntegrationTransports(
sessionId,
userId,
workspaceId,
integrations.length > 0 ? integrations : undefined,
);
logger.log(
`Loaded ${result.loaded} integration transports for session ${sessionId}`,
);
if (result.failed.length > 0) {
logger.warn(`Failed to load some integrations: ${result.failed}`);
}
}
} catch (error) {
logger.error(`Error loading integration transports: ${error}`);
}
// Create and connect MCP server
const server = await createMcpServer(userId, sessionId);
const server = await createMcpServer(userId, sessionId, source);
await server.connect(transport);
return transport;
@ -183,6 +189,8 @@ export const handleMCPRequest = async (
? queryParams.integrations.split(",").map((s) => s.trim())
: [];
const noIntegrations = queryParams.no_integrations ?? false;
const userId = authentication.userId;
const workspace = await getWorkspaceByUser(userId);
const workspaceId = workspace?.id as string;
@ -206,6 +214,7 @@ export const handleMCPRequest = async (
sessionId,
sessionDetails.source,
sessionDetails.integrations,
noIntegrations,
userId,
workspaceId,
);
@ -222,6 +231,7 @@ export const handleMCPRequest = async (
currentSessionId,
source,
integrations,
noIntegrations,
userId,
workspaceId,
);

View File

@ -66,13 +66,14 @@ export async function callMemoryTool(
toolName: string,
args: any,
userId: string,
source: string,
) {
try {
switch (toolName) {
case "memory_ingest":
return await handleMemoryIngest({ ...args, userId });
return await handleMemoryIngest({ ...args, userId, source });
case "memory_search":
return await handleMemorySearch({ ...args, userId });
return await handleMemorySearch({ ...args, userId, source });
case "memory_get_spaces":
return await handleMemoryGetSpaces(userId);
default:

View File

@ -82,6 +82,7 @@ export class MCPSessionManager {
id: session.id,
source: session.source,
integrations: session.integrations,
noIntegrations: session.noIntegrations,
createdAt: session.createdAt,
deleted: session.deleted || undefined,
workspaceId: session.workspaceId || undefined,