Native library reference. Extended CLI examples use
jacs-compat. This source reference can include unreleased changes; see the package guide for installation and MCP profiles for available tools.
MCP Integration (Node.js)
Source and published packages: Version 0.16.1 has a recorded release inventory. Version 0.16.0 is the first release whose source includes the native PQ DID/JWK and Agent Card behavior. See the identity integration guide.
MCP is the primary integration for external model clients. For the portable
server, install jacs-cli and run jacs mcp; it starts in verify-only mode.
See the current MCP guide. The separate native compatibility
server is jacs-compat mcp from jacs-cli-compat.
Inside a Node application, choose between signed tool results on ordinary MCP and the JACS transport extension. Signing an output does not authenticate its recipient or authorize a tool call.
npm install @hai.ai/jacs @modelcontextprotocol/sdk
Signed tool results on ordinary MCP
This ES module (server.mjs) registers a read-only echo tool that signs its
result. The SDK uses request schemas, not string method names, for handlers:
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import { ListToolsRequestSchema, CallToolRequestSchema } from '@modelcontextprotocol/sdk/types.js';
import { JacsClient } from '@hai.ai/jacs/client';
const client = await JacsClient.quickstart({
name: 'echo-server', domain: 'mcp.local',
configPath: './server/jacs.config.json',
});
const server = new Server(
{ name: 'signed-echo', version: '1.0.0' },
{ capabilities: { tools: {} } },
);
server.setRequestHandler(ListToolsRequestSchema, async () => ({
tools: [{
name: 'echo', description: 'Return signed echo data',
inputSchema: {
type: 'object', properties: { message: { type: 'string' } },
required: ['message'], additionalProperties: false,
},
}],
}));
server.setRequestHandler(CallToolRequestSchema, async (request) => {
if (request.params.name !== 'echo') throw new Error('Unknown tool');
const message = request.params.arguments?.message;
if (typeof message !== 'string') throw new Error('message must be a string');
const signed = await client.signMessage({ echo: message });
return { content: [{ type: 'text', text: signed.raw }] };
});
await server.connect(new StdioServerTransport());
An ordinary MCP client can call this tool. Its relying application must verify the returned document against its own trusted keys before using the contents. Keep stdio diagnostics on stderr; stdout carries protocol messages only.
Signed transport between JACS peers
Both peers must use the JACS transport wrapper. This is a local-only extension,
not a transparent way to connect arbitrary MCP clients. The implementation
rejects localOnly: false.
Provision each peer's identity and verification key through a trusted channel
before connecting. Set one exact expectedPeerAgentId, or an intentional
allowedPeerAgentIds allowlist, on each wrapper. A missing policy rejects
incoming authenticated messages. An optional expectedPeerPublicKeyHash
additionally pins the key for one exact peer.
Replace the final connection in the server above with:
import { createJACSTransportProxy } from '@hai.ai/jacs/mcp';
const expectedPeerAgentId = process.env.JACS_EXPECTED_CLIENT_ID;
if (!expectedPeerAgentId) throw new Error('Configure the trusted client agent ID');
const transport = createJACSTransportProxy(
new StdioServerTransport(), client, 'server', { expectedPeerAgentId },
);
await server.connect(transport);
The matching client wraps its StdioClientTransport the same way, using role
'client' and the trusted server ID. Use separate configuration directories
for server and client so quickstart does not reload the same identity. Load
verification keys into the local trust/key store before starting; these options
select an allowed identity, they do not fetch its key.
When only an existing config path is available:
import { createJACSTransportProxyAsync } from '@hai.ai/jacs/mcp';
const transport = await createJACSTransportProxyAsync(
baseTransport, './client/jacs.config.json', 'client',
{ expectedPeerAgentId: trustedServerId },
);
The synchronous factory takes a loaded JacsClient or JacsAgent, never a
config path. Signing, signature checks, peer checks and message dispatch fail
closed by default. Do not use allowUnsignedFallback or
dangerouslyAllowAnyValidSigner when authenticated peer identity is required.
Signatures provide integrity and key possession, not encryption. Application
permissions and replay policy remain separate from signature validity.
Registering compatibility tools
registerJacsTools(server, client) installs the Node compatibility tool list
and SDK handlers on a low-level Server. It replaces the list/call handlers,
so do not combine it with the echo handlers above. The helper includes signing,
agreement and trust-store mutations as well as inspection; it does not inherit
the portable server's verify-only profile. Expose only operations authorized
for your callers.
For a selected tool list, use getJacsMcpToolDefinitions() and
handleJacsMcpToolCall(client, name, args) behind your own SDK schema handlers
and allowlist. Some setup/registry entries are stubs; listing an operation
does not establish that it implements the full Rust server contract.
For a model already running inside Node, see LangChain.js or Vercel AI SDK. For protocol-wide concepts, see the native MCP guide.