@elsium-ai/mcp

Model Context Protocol (MCP) support for ElsiumAI — bidirectional client and server over stdio transport.

Install

npm install @elsium-ai/mcp

Peer dependencies @elsium-ai/core and @elsium-ai/tools are pulled in automatically when you install within the ElsiumAI monorepo. In standalone usage, install them explicitly:

npm install @elsium-ai/mcp @elsium-ai/core @elsium-ai/tools

What’s Inside

Category Export Kind Description
Client createMCPClient function Create an MCP client that connects to an external MCP server over stdio
MCPClient interface Shape of the object returned by createMCPClient
MCPClientConfig interface Configuration for createMCPClient
MCPToolInfo interface Metadata for a single tool reported by an MCP server
Server createMCPServer function Create an MCP server that exposes ElsiumAI tools over stdio
MCPServer interface Shape of the object returned by createMCPServer
MCPServerConfig interface Configuration for createMCPServer

Client

MCPClientConfig

MCPClientConfig is a discriminated union over the transport field, with one variant per transport. createMCPClient selects the implementation based on transport.

type MCPClientConfig = MCPClientStdioConfig | MCPClientHttpConfig

MCPClientStdioConfig — connect to a server spawned as a child process:

interface MCPClientStdioConfig {
  name: string            // Client identifier
  transport: 'stdio'      // Transport discriminant
  command: string         // Command to spawn the server process
  args?: string[]         // Command arguments
  env?: Record<string, string>  // Environment variables for the process
  timeoutMs?: number      // Request timeout (default: 30000)
}
Property Type Required Default Description
name string yes — Logical name for this client connection (used in the protocol handshake)
transport 'stdio' yes — Transport discriminant selecting the stdio variant
command string yes — The command to spawn the MCP server subprocess (e.g. "npx", "node")
args string[] no [] Arguments passed to the spawned command
env Record<string, string> no {} Additional environment variables for the subprocess. PATH and HOME are inherited automatically
timeoutMs number no 30000 Timeout in milliseconds for each JSON-RPC request

MCPClientHttpConfig — connect to a server over HTTP JSON-RPC:

interface MCPClientHttpConfig {
  name: string            // Client identifier
  transport: 'http'       // Transport discriminant
  url: string             // Server endpoint URL
  headers?: Record<string, string>  // Extra headers sent with every request
  timeoutMs?: number      // Request timeout (default: 30000)
}
Property Type Required Default Description
name string yes — Logical name for this client connection (used in the protocol handshake)
transport 'http' yes — Transport discriminant selecting the HTTP variant
url string yes — Server endpoint URL the client posts JSON-RPC messages to
headers Record<string, string> no {} Extra headers sent with every request
timeoutMs number no 30000 Timeout in milliseconds for each JSON-RPC request

MCPToolInfo

Describes a single tool as reported by an MCP server’s tools/list response.

interface MCPToolInfo {
  name: string
  description: string
  inputSchema: Record<string, unknown>
}

MCPClient

The interface returned by createMCPClient. Provides methods for connecting to an MCP server, discovering its tools, calling them, and converting them into ElsiumAI-compatible Tool objects.

interface MCPClient {
  connect(): Promise<void>
  disconnect(): Promise<void>
  listTools(): Promise<MCPToolInfo[]>
  callTool(name: string, args: Record<string, unknown>): Promise<unknown>
  toElsiumTools(): Promise<Tool[]>
  listResources(): Promise<MCPResource[]>
  readResource(uri: string): Promise<MCPResourceContent[]>
  listPrompts(): Promise<MCPPrompt[]>
  getPrompt(name: string, args?: Record<string, string>): Promise<MCPPromptMessage[]>
  readonly connected: boolean
}
Member Description
connected Read-only boolean indicating whether the client is currently connected
connect() Spawn the subprocess, perform the MCP initialize handshake, and send the notifications/initialized notification
disconnect() Terminate the subprocess, reject all pending requests, and clean up
listTools() Send a tools/list request and return the available tools
callTool(name, args) Invoke a tool on the remote server. Returns the concatenated text content from the response
toElsiumTools() List all remote tools and wrap each one as an ElsiumAI Tool, ready to be passed to an agent
listResources() List resources exposed by the server (resources/list)
readResource(uri) Read the contents of a resource by URI (resources/read)
listPrompts() List prompt templates exposed by the server (prompts/list)
getPrompt(name, args?) Resolve a prompt template into messages, substituting args (prompts/get)

createMCPClient(config)

Create a new MCP client. The client does not connect automatically — call connect() before using it.

function createMCPClient(config: MCPClientConfig): MCPClient

Parameters

Name Type Description
config MCPClientConfig Client configuration (see above)

Returns — MCPClient

Example — connect to an MCP server and list its tools

import { createMCPClient } from '@elsium-ai/mcp'

const client = createMCPClient({
  name: 'filesystem',
  transport: 'stdio',
  command: 'npx',
  args: ['-y', '@modelcontextprotocol/server-filesystem', '/tmp'],
})

await client.connect()

const tools = await client.listTools()
console.log(tools)
// [{ name: 'read_file', description: '...', inputSchema: { ... } }, ...]

await client.disconnect()

Example — call a remote tool directly

import { createMCPClient } from '@elsium-ai/mcp'

const client = createMCPClient({
  name: 'filesystem',
  transport: 'stdio',
  command: 'npx',
  args: ['-y', '@modelcontextprotocol/server-filesystem', '/tmp'],
})

await client.connect()

const content = await client.callTool('read_file', { path: '/tmp/hello.txt' })
console.log(content) // file contents as a string

await client.disconnect()

Example — convert MCP tools into ElsiumAI tools for an agent

import { createMCPClient } from '@elsium-ai/mcp'

const client = createMCPClient({
  name: 'filesystem',
  transport: 'stdio',
  command: 'npx',
  args: ['-y', '@modelcontextprotocol/server-filesystem', '/tmp'],
})

await client.connect()

// Each returned Tool has execute() and toDefinition() wired through the MCP client
const elsiumTools = await client.toElsiumTools()

// Pass them to an agent, a toolkit, or call execute() directly
const result = await elsiumTools[0].execute({ path: '/tmp/hello.txt' })
console.log(result)
// { success: true, data: '...', toolCallId: 'tc_...', durationMs: 42 }

Server

MCPServerConfig

Configuration object passed to createMCPServer.

interface MCPServerConfig {
  name: string                       // Server name
  version?: string                   // Server version
  tools: Tool[]                      // ElsiumAI tools to expose via MCP
  resources?: MCPResourceHandler[]   // Resources to expose via MCP
  prompts?: MCPPromptHandler[]       // Prompt templates to expose via MCP
}
Property Type Required Default Description
name string yes — Server name reported in the initialize handshake
version string no '0.1.0' Server version reported in the initialize handshake
tools Tool[] yes — Array of ElsiumAI Tool objects to expose over MCP
resources MCPResourceHandler[] no [] Resources to expose via MCP. When provided, the server advertises the resources capability and serves resources/list and resources/read
prompts MCPPromptHandler[] no [] Prompt templates to expose via MCP. When provided, the server advertises the prompts capability and serves prompts/list and prompts/get

MCPServer

The interface returned by createMCPServer. The server reads JSON-RPC requests from stdin and writes responses to stdout, implementing the MCP protocol over stdio transport.

interface MCPServer {
  start(): Promise<void>
  stop(): void
  readonly running: boolean
}
Member Description
running Read-only boolean indicating whether the server is currently listening for requests
start() Begin listening on stdin for incoming JSON-RPC messages. Handles initialize, notifications/initialized, tools/list, and tools/call
stop() Stop the server by setting the running flag to false

createMCPServer(config)

Create a new MCP server that exposes the provided ElsiumAI tools over the MCP protocol.

function createMCPServer(config: MCPServerConfig): MCPServer

Parameters

Name Type Description
config MCPServerConfig Server configuration (see above)

Returns — MCPServer

Example — expose ElsiumAI tools as an MCP server

import { createMCPServer } from '@elsium-ai/mcp'
import { createTool } from '@elsium-ai/tools'
import { z } from 'zod'

const greet = createTool({
  name: 'greet',
  description: 'Return a greeting for the given name',
  input: z.object({ name: z.string() }),
  execute: async ({ input }) => `Hello, ${input.name}!`,
})

const server = createMCPServer({
  name: 'my-tools',
  version: '1.0.0',
  tools: [greet],
})

await server.start()
// The server is now listening on stdin/stdout.
// MCP clients (e.g. Claude Desktop) can connect to it via stdio transport.

Types

Export Description
MCPClient Client interface: tool, resource, and prompt operations plus connected
MCPClientConfig Discriminated union of MCPClientStdioConfig and MCPClientHttpConfig
MCPClientStdioConfig Stdio client config: name, transport, command, args?, env?, timeoutMs?
MCPClientHttpConfig HTTP client config: name, transport, url, headers?, timeoutMs?
MCPToolInfo Tool info returned by listTools: name, description, inputSchema
MCPServer Server interface: start, stop, running
MCPServerConfig Server config: name, version?, tools, resources?, prompts?
MCPHttpHandler HTTP handler function: (request: Request) => Promise<Response>
MCPHttpHandlerConfig HTTP handler config: name, version?, tools, resources?, prompts?
MCPResource Resource metadata: uri, name, description?, mimeType?
MCPResourceContent Resource contents: uri, mimeType?, text?, blob?
MCPResourceHandler Server-side resource definition with a read() callback
MCPPrompt Prompt metadata: name, description?, arguments?
MCPPromptArgument Prompt argument declaration: name, description?, required?
MCPPromptMessage Prompt message: role ('user' | 'assistant'), content ({ type: 'text'; text: string })
MCPPromptHandler Server-side prompt definition with a get() callback
MCPTransport Transport identifier: 'stdio' | 'http'

MCP Trust Framework

Security layer for MCP connections: server allowlists, tool filtering, output validation, manifest integrity, and audit logging.

createTrustedMCPClient

createTrustedMCPClient(config: MCPClientConfig, trustConfig: MCPTrustConfig): TrustedMCPClient

Wraps a standard MCP client with security controls.

MCPTrustConfig:

Field Type Description
allowedServers AllowedServer[]? Server allowlist
validateToolOutputs boolean? Enable output validation
auditLog MCPAuditLogger? Logger for MCP operations
maxToolOutputSize number? Max tool output size (default: 1MB)

AllowedServer:

Field Type Description
name string Server name
transport 'stdio' | 'http' Expected transport
commandHash string? SHA-256 hash for stdio
urlPattern string? Regex for HTTP URL
allowedTools string[]? Tool whitelist
deniedTools string[]? Tool blacklist
import { createTrustedMCPClient } from '@elsium-ai/mcp'

const client = createTrustedMCPClient(
  { name: 'data-tools', transport: 'http', url: 'http://localhost:3001/mcp' },
  {
    allowedServers: [
      {
        name: 'data-tools',
        transport: 'http',
        urlPattern: '^http://localhost:3001',
        allowedTools: ['query_db', 'list_tables'],
        deniedTools: ['drop_table'],
      },
    ],
    maxToolOutputSize: 512 * 1024,
    auditLog: { log: (event) => console.log(event) },
  },
)

await client.connect()
const tools = await client.listTools() // only returns allowed tools

Tool Manifests

Generate and verify tool manifests for supply chain integrity:

const manifest = await client.generateManifest()
// { serverName, tools: [{ name, description, inputSchemaHash }], hash }

const isValid = await client.verifyManifest(manifest)
// false if server tools have changed since manifest was generated

Types

Export Description
TrustedMCPClient Extended MCPClient with manifest support
MCPTrustConfig Trust configuration
AllowedServer Server allowlist entry
MCPAuditLogger Audit logger interface
MCPAuditEvent Audit event type
MCPToolManifest Tool manifest with hash

Resources & Prompts

Beyond tools, an MCP server may expose resources (readable data sources identified by URI) and prompts (named, parameterized message templates). A connected MCPClient can list and read them:

// Resources
const resources = await client.listResources()
// => [{ uri: 'file:///docs/readme.md', name: 'README', mimeType: 'text/markdown' }, ...]

const contents = await client.readResource('file:///docs/readme.md')
// => [{ uri: 'file:///docs/readme.md', mimeType: 'text/markdown', text: '# ...' }]

// Prompts
const prompts = await client.listPrompts()
// => [{ name: 'summarize', description: '...', arguments: [{ name: 'topic', required: true }] }]

const messages = await client.getPrompt('summarize', { topic: 'quarterly results' })
// => [{ role: 'user', content: { type: 'text', text: 'Summarize quarterly results' } }]

MCPResourceHandler

Server-side definition of a resource exposed via the resources option of MCPServerConfig.

interface MCPResourceHandler {
  uri: string           // Unique resource identifier
  name: string          // Human-readable name
  description?: string  // Resource description
  mimeType?: string     // MIME type of the content
  read: () => Promise<string | { text?: string; blob?: string }>
}

read() may return a plain string (treated as text) or an object with text and/or base64 blob content.

MCPPromptHandler

Server-side definition of a prompt template exposed via the prompts option of MCPServerConfig.

interface MCPPromptHandler {
  name: string                       // Unique prompt name
  description?: string               // Prompt description
  arguments?: MCPPromptArgument[]    // Declared template arguments
  get: (args?: Record<string, string>) => Promise<MCPPromptMessage[]>
}

Exposing Resources & Prompts

When resources or prompts are provided to createMCPServer, the server advertises the corresponding capabilities during initialize and serves resources/list, resources/read, prompts/list, and prompts/get requests.

const server = createMCPServer({
  name: 'docs-server',
  tools: [],
  resources: [
    {
      uri: 'file:///docs/readme.md',
      name: 'README',
      mimeType: 'text/markdown',
      read: async () => '# Project docs\n...',
    },
  ],
  prompts: [
    {
      name: 'summarize',
      description: 'Summarize a topic',
      arguments: [{ name: 'topic', required: true }],
      get: async (args) => [
        {
          role: 'user',
          content: { type: 'text', text: `Summarize ${args?.topic}` },
        },
      ],
    },
  ],
})

await server.start()

HTTP Handler

createMCPHttpHandler returns a function that maps a standard Request to a Response, so it can be mounted in any runtime that speaks the web Fetch API (edge functions, Bun, Deno, Node with an adapter, etc.). It exposes the same tools, resources, and prompts as the stdio server but does not manage a process lifecycle.

Export Signature Description
createMCPHttpHandler createMCPHttpHandler(config: MCPHttpHandlerConfig): MCPHttpHandler Create a transport-agnostic request handler that serves the MCP protocol over HTTP JSON-RPC

MCPHttpHandlerConfig

interface MCPHttpHandlerConfig {
  name: string                       // Server name
  version?: string                   // Server version
  tools: Tool[]                      // ElsiumAI tools to expose via MCP
  resources?: MCPResourceHandler[]   // Resources to expose via MCP
  prompts?: MCPPromptHandler[]       // Prompt templates to expose via MCP
}

MCPHttpHandler

type MCPHttpHandler = (request: Request) => Promise<Response>

Only POST requests are accepted (others return 405). The request body is parsed as a single JSON-RPC message; malformed JSON returns a -32700 parse error. Notifications (e.g. notifications/initialized) produce a 204 No Content response.

Example

import { createMCPHttpHandler, createMCPClient } from '@elsium-ai/mcp'

const handler = createMCPHttpHandler({
  name: 'math-tools',
  version: '1.0.0',
  tools: [calculator],
})

// Mount in any Fetch-API runtime
export default { fetch: handler }

// ...or pair with an MCP HTTP client
const client = createMCPClient({
  name: 'math-tools',
  transport: 'http',
  url: 'http://localhost:3000/mcp',
})
await client.connect()

Resource & Prompt Types

Shared metadata and content types returned by the client resource and prompt methods.

interface MCPResource {
  uri: string
  name: string
  description?: string
  mimeType?: string
}

interface MCPResourceContent {
  uri: string
  mimeType?: string
  text?: string
  blob?: string   // base64-encoded binary content
}

interface MCPPrompt {
  name: string
  description?: string
  arguments?: MCPPromptArgument[]
}

interface MCPPromptArgument {
  name: string
  description?: string
  required?: boolean
}

interface MCPPromptMessage {
  role: 'user' | 'assistant'
  content: { type: 'text'; text: string }
}

Capability Guard

Gates callTool on a CapabilityToken, so an MCP client can only invoke tools the token is authorized to use. Every other method (connect, listTools, readResource, etc.) is passed through unchanged.

createCapabilityGuardedMCPClient

createCapabilityGuardedMCPClient(
  client: MCPClient,
  options: CapabilityGuardedMCPOptions,
): MCPClient

Wraps an existing MCPClient. On each callTool, it optionally verifies the token via options.verifier, then checks the token’s scope against { server, tool } with canUseMcp. A denied call throws an ElsiumError with code AUTH_ERROR and (if provided) invokes options.onDeny.

CapabilityGuardedMCPOptions

Field Type Description
token CapabilityToken Token authorizing tool calls
server string Server name used for scope checks
verifier CapabilityVerifier? Optional verifier run before the scope check
onDeny (event: MCPCapabilityDenialEvent) => void? Callback fired when a call is denied

MCPCapabilityDenialEvent

Field Type Description
tokenId string ID of the denied token
subject string Agent subject from the token
server string Server name
tool string Tool that was denied
reason CapabilityCheckReason | undefined Why the call was denied
detail string? Additional detail
import { createMCPClient, createCapabilityGuardedMCPClient } from '@elsium-ai/mcp'

const guarded = createCapabilityGuardedMCPClient(
  createMCPClient({ name: 'data-tools', transport: 'http', url: 'http://localhost:3001/mcp' }),
  {
    token,
    server: 'data-tools',
    onDeny: (event) => console.warn('MCP call denied', event),
  },
)

await guarded.connect()
await guarded.callTool('query_db', { sql: 'SELECT 1' }) // throws AUTH_ERROR if not authorized

Types

Export Description
CapabilityGuardedMCPOptions Guard options: token, server, verifier?, onDeny?
MCPCapabilityDenialEvent Denial event passed to onDeny

Part of ElsiumAI

This package is the MCP (Model Context Protocol) layer of the ElsiumAI framework. See the Getting Started guide or the Fundamentals for comprehensive coverage of every feature.

License

MIT