MetaMCP MCP Server
Official
by metatool-ai
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import {
CallToolRequestSchema,
GetPromptRequestSchema,
ListPromptsRequestSchema,
ListResourcesRequestSchema,
ListToolsRequestSchema,
ReadResourceRequestSchema,
Tool,
ListToolsResultSchema,
ListPromptsResultSchema,
ListResourcesResultSchema,
ReadResourceResultSchema,
ListResourceTemplatesRequestSchema,
ListResourceTemplatesResultSchema,
ResourceTemplate,
CompatibilityCallToolResultSchema,
GetPromptResultSchema,
} from "@modelcontextprotocol/sdk/types.js";
import { z } from "zod";
import { getMcpServers } from "./fetch-metamcp.js";
import { getSessionKey, sanitizeName } from "./utils.js";
import { cleanupAllSessions, getSession, initSessions } from "./sessions.js";
import { ConnectedClient } from "./client.js";
import { reportToolsToMetaMcp } from "./report-tools.js";
import { getInactiveTools, ToolParameters } from "./fetch-tools.js";
import {
getProfileCapabilities,
ProfileCapability,
} from "./fetch-capabilities.js";
const toolToClient: Record<string, ConnectedClient> = {};
const promptToClient: Record<string, ConnectedClient> = {};
const resourceToClient: Record<string, ConnectedClient> = {};
const inactiveToolsMap: Record<string, boolean> = {};
export const createServer = async () => {
const server = new Server(
{
name: "MetaMCP",
version: "0.4.2",
},
{
capabilities: {
prompts: {},
resources: {},
tools: {},
},
}
);
// Initialize sessions in the background when server starts
initSessions().catch();
// List Tools Handler
server.setRequestHandler(ListToolsRequestSchema, async (request) => {
const profileCapabilities = await getProfileCapabilities(true);
const serverParams = await getMcpServers(true);
// Fetch inactive tools only if tools management capability is present
let inactiveTools: Record<string, ToolParameters> = {};
if (profileCapabilities.includes(ProfileCapability.TOOLS_MANAGEMENT)) {
inactiveTools = await getInactiveTools(true);
// Clear existing inactive tools map before rebuilding
Object.keys(inactiveToolsMap).forEach(
(key) => delete inactiveToolsMap[key]
);
}
const allTools: Tool[] = [];
await Promise.allSettled(
Object.entries(serverParams).map(async ([uuid, params]) => {
const sessionKey = getSessionKey(uuid, params);
const session = await getSession(sessionKey, uuid, params);
if (!session) return;
const capabilities = session.client.getServerCapabilities();
if (!capabilities?.tools) return;
const serverName = session.client.getServerVersion()?.name || "";
try {
const result = await session.client.request(
{
method: "tools/list",
params: { _meta: request.params?._meta },
},
ListToolsResultSchema
);
const toolsWithSource =
result.tools
?.filter((tool) => {
// Only filter inactive tools if tools management is enabled
if (
profileCapabilities.includes(
ProfileCapability.TOOLS_MANAGEMENT
)
) {
return !inactiveTools[`${uuid}:${tool.name}`];
}
return true;
})
.map((tool) => {
const toolName = `${sanitizeName(serverName)}__${tool.name}`;
toolToClient[toolName] = session;
return {
...tool,
name: toolName,
description: `[${serverName}] ${tool.description || ""}`,
};
}) || [];
// Update our inactive tools map only if tools management is enabled
if (
profileCapabilities.includes(ProfileCapability.TOOLS_MANAGEMENT)
) {
result.tools?.forEach((tool) => {
const isInactive = inactiveTools[`${uuid}:${tool.name}`];
if (isInactive) {
const formattedName = `${sanitizeName(serverName)}__${
tool.name
}`;
inactiveToolsMap[formattedName] = true;
}
});
// Report full tools for this server
reportToolsToMetaMcp(
result.tools.map((tool) => ({
name: tool.name,
description: tool.description,
toolSchema: tool.inputSchema,
mcp_server_uuid: uuid,
}))
).catch();
}
allTools.push(...toolsWithSource);
} catch (error) {
console.error(`Error fetching tools from: ${serverName}`, error);
}
})
);
return { tools: allTools };
});
// Call Tool Handler
server.setRequestHandler(CallToolRequestSchema, async (request) => {
const { name, arguments: args } = request.params;
const clientForTool = toolToClient[name];
if (!clientForTool) {
throw new Error(`Unknown tool: ${name}`);
}
// Only check inactive tools if tools management capability is present
const profileCapabilities = await getProfileCapabilities();
if (
profileCapabilities.includes(ProfileCapability.TOOLS_MANAGEMENT) &&
inactiveToolsMap[name]
) {
throw new Error(`Tool is inactive: ${name}`);
}
try {
const toolName = name.split("__")[1];
// Use the correct schema for tool calls
return await clientForTool.client.request(
{
method: "tools/call",
params: {
name: toolName,
arguments: args || {},
_meta: {
progressToken: request.params._meta?.progressToken,
},
},
},
CompatibilityCallToolResultSchema
);
} catch (error) {
console.error(
`Error calling tool through ${
clientForTool.client.getServerVersion()?.name
}:`,
error
);
throw error;
}
});
// Get Prompt Handler
server.setRequestHandler(GetPromptRequestSchema, async (request) => {
const { name } = request.params;
const clientForPrompt = promptToClient[name];
if (!clientForPrompt) {
throw new Error(`Unknown prompt: ${name}`);
}
try {
const promptName = name.split("__")[1];
const response = await clientForPrompt.client.request(
{
method: "prompts/get",
params: {
name: promptName,
arguments: request.params.arguments || {},
_meta: request.params._meta,
},
},
GetPromptResultSchema
);
return response;
} catch (error) {
console.error(
`Error getting prompt through ${
clientForPrompt.client.getServerVersion()?.name
}:`,
error
);
throw error;
}
});
// List Prompts Handler
server.setRequestHandler(ListPromptsRequestSchema, async (request) => {
const serverParams = await getMcpServers(true);
const allPrompts: z.infer<typeof ListPromptsResultSchema>["prompts"] = [];
await Promise.allSettled(
Object.entries(serverParams).map(async ([uuid, params]) => {
const sessionKey = getSessionKey(uuid, params);
const session = await getSession(sessionKey, uuid, params);
if (!session) return;
const capabilities = session.client.getServerCapabilities();
if (!capabilities?.prompts) return;
const serverName = session.client.getServerVersion()?.name || "";
try {
const result = await session.client.request(
{
method: "prompts/list",
params: {
cursor: request.params?.cursor,
_meta: request.params?._meta,
},
},
ListPromptsResultSchema
);
if (result.prompts) {
const promptsWithSource = result.prompts.map((prompt) => {
const promptName = `${sanitizeName(serverName)}__${prompt.name}`;
promptToClient[promptName] = session;
return {
...prompt,
name: promptName,
description: `[${serverName}] ${prompt.description || ""}`,
};
});
allPrompts.push(...promptsWithSource);
}
} catch (error) {
console.error(`Error fetching prompts from: ${serverName}`, error);
}
})
);
return {
prompts: allPrompts,
nextCursor: request.params?.cursor,
};
});
// List Resources Handler
server.setRequestHandler(ListResourcesRequestSchema, async (request) => {
const serverParams = await getMcpServers(true);
const allResources: z.infer<typeof ListResourcesResultSchema>["resources"] =
[];
await Promise.allSettled(
Object.entries(serverParams).map(async ([uuid, params]) => {
const sessionKey = getSessionKey(uuid, params);
const session = await getSession(sessionKey, uuid, params);
if (!session) return;
const capabilities = session.client.getServerCapabilities();
if (!capabilities?.resources) return;
const serverName = session.client.getServerVersion()?.name || "";
try {
const result = await session.client.request(
{
method: "resources/list",
params: {
cursor: request.params?.cursor,
_meta: request.params?._meta,
},
},
ListResourcesResultSchema
);
if (result.resources) {
const resourcesWithSource = result.resources.map((resource) => {
resourceToClient[resource.uri] = session;
return {
...resource,
name: `[${serverName}] ${resource.name || ""}`,
};
});
allResources.push(...resourcesWithSource);
}
} catch (error) {
console.error(`Error fetching resources from: ${serverName}`, error);
}
})
);
return {
resources: allResources,
nextCursor: request.params?.cursor,
};
});
// Read Resource Handler
server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
const { uri } = request.params;
const clientForResource = resourceToClient[uri];
if (!clientForResource) {
throw new Error(`Unknown resource: ${uri}`);
}
try {
return await clientForResource.client.request(
{
method: "resources/read",
params: {
uri,
_meta: request.params._meta,
},
},
ReadResourceResultSchema
);
} catch (error) {
console.error(
`Error reading resource through ${
clientForResource.client.getServerVersion()?.name
}:`,
error
);
throw error;
}
});
// List Resource Templates Handler
server.setRequestHandler(
ListResourceTemplatesRequestSchema,
async (request) => {
const serverParams = await getMcpServers(true);
const allTemplates: ResourceTemplate[] = [];
await Promise.allSettled(
Object.entries(serverParams).map(async ([uuid, params]) => {
const sessionKey = getSessionKey(uuid, params);
const session = await getSession(sessionKey, uuid, params);
if (!session) return;
const capabilities = session.client.getServerCapabilities();
if (!capabilities?.resources) return;
const serverName = session.client.getServerVersion()?.name || "";
try {
const result = await session.client.request(
{
method: "resources/templates/list",
params: {
cursor: request.params?.cursor,
_meta: request.params?._meta,
},
},
ListResourceTemplatesResultSchema
);
if (result.resourceTemplates) {
const templatesWithSource = result.resourceTemplates.map(
(template) => ({
...template,
name: `[${serverName}] ${template.name || ""}`,
})
);
allTemplates.push(...templatesWithSource);
}
} catch (error) {
return;
}
})
);
return {
resourceTemplates: allTemplates,
nextCursor: request.params?.cursor,
};
}
);
const cleanup = async () => {
await cleanupAllSessions();
};
return { server, cleanup };
};