@arnilo/prism-mcp
v0.3.0
Published
Bounded MCP tools, resources, prompts, client callbacks, and authorized Streamable HTTP sessions.
Maintainers
Readme
@arnilo/prism-mcp
Bounded MCP client capabilities and explicit Prism MCP server exposure, pinned to official SDK 1.30.0. Client direction connects over stdio or Streamable HTTP and maps discovered tools to ToolDefinitions. Server direction registers selected Prism tools/commands on SDK McpServer, with required authorization and an optional bounded web-standard handler. Shared elicitation helpers (mcpElicitationDecision / mcpElicitationResultFromDecision) map onto Prism pending decisions and require host humanInteraction: true on accept. Server guardrails apply shared core tool stages to registered tools; commands remain host callbacks.
Install
npm install @arnilo/prism-mcp @arnilo/prismUsage
import { createAgent } from "@arnilo/prism";
import { connectMcpTools } from "@arnilo/prism-mcp";
const bridge = await connectMcpTools({
serverId: "fs",
transport: {
type: "stdio",
command: "node",
args: ["path/to/mcp-server.js"],
},
});
const agent = createAgent({
model,
tools: bridge.tools,
});
// When finished:
await bridge.close();Streamable HTTP:
const bridge = await connectMcpTools({
serverId: "remote",
transport: {
type: "streamable-http",
url: "https://mcp.example.com/mcp",
allowedOrigins: ["https://mcp.example.com"],
requestInit: {
headers: { Authorization: "Bearer <token>" },
},
},
});Remote tool names are prefixed as mcp:<serverId>:<toolName> by default to avoid registry collisions. Use connectMcpCapabilities() for separate bounded resources/prompts plus explicit host roots/sampling/elicitation callbacks; missing capability calls throw ERR_PRISM_MCP_UNSUPPORTED_CAPABILITY.
MCP Apps requires explicit negotiation, not metadata guessing:
const bridge = await connectMcpTools({ serverId: "weather", transport, mcpApps: true });
const resource = await bridge.apps!.readResource("ui://weather/card");mcpApps: true advertises and requires io.modelcontextprotocol/ui. bridge.tools excludes app-only tools; bridge.apps preserves UI metadata, permits linked bounded HTML resource reads, and calls only same-server app-visible tools. Use @arnilo/prism-ag-ui to mount an authenticated proxy and separate-origin renderer sandbox; this package never renders HTML.
Server exposure
import { createPrismMcpServer, createPrismMcpWebHandler } from "@arnilo/prism-mcp";
const server = createPrismMcpServer({
tools: [approvedTool],
commands: [approvedWorkflowCommand],
authorize: async ({ authInfo }) => hostAllows(authInfo)
? { allowed: true, ownership: { tenantId: "tenant-1" } }
: false,
validate,
permission,
redactor,
});
const handleMcp = await createPrismMcpWebHandler(server, { resolveAuthInfo });
// Stateful mode additionally requires sessionIdGenerator, exact allowedOrigins,
// and resolveIdentity to bind every POST/GET/DELETE/SSE request to one principal.Nothing is exposed by default. To expose a durable agent, pass agentRuns: { support: { lifecycle: createAgentRunLifecycle({ checkpoints, resolveAgent }) } }; this registers only agent.support.status and agent.support.resume under normal MCP authorization. Handler uses SDK Web-standard Streamable HTTP transport; no listener or auth provider starts. Request/result/concurrency/timeouts are bounded. Use server.connect() directly for SDK stdio/in-memory transports.
Security
- Stdio command, args, env, and cwd are explicit host configuration — Prism does not auto-launch unknown servers.
- Streamable HTTP requires HTTPS plus an exact
allowedOriginsentry. Every POST/GET/DELETE/reconnect resolves all DNS answers, rejects mixed/private results, pins one public address, rejects credentials/fragments/redirects, and bounds each response. Plaintext requiresallowLoopbackHttp: trueand loopback-only DNS. - Discovery defaults to 20 pages/500 tools, finite metadata/schema budgets, and atomic refresh. Raw SDK list/call requests avoid compiling untrusted output schemas.
- Every result branch (
content,structuredContent, legacytoolResult) sharesmaxResultBytes, JSON depth, and property bounds beforeToolResultretention. - Register returned tools only after reviewing server trust; core
PermissionPolicyandToolValidatorstill apply at dispatch. - MCP Apps keeps nested metadata over deprecated flat metadata, permits only linked
ui://text/html;profile=mcp-appHTML5 bodies, and leaves iframe sandbox/CSP, app-call approval, and mutation recovery to the host/AG-UI adapter. - Server resources/prompts re-authorize every callback. Sampling/model choice, roots, credentials, and form/URL consent remain host-owned; Prism never opens elicitation URLs. Stateful sessions bind a non-secret principal ID and disclose mismatches only as 404.
See MCP client/server exposure and Tool execution primitives.
