@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.