Skip to content

Quickstart: your first MCP tool

Build a small MCP server, register it in DioscHub, and end with a DioscHub Assistant calling your tool as the signed-in user. The user’s auth is forwarded to your server on each call, and your server authorizes the request.

  • A running DioscHub deployment and admin access to its portal.
  • Node.js 18+ and TypeScript.
  • A reachable URL where DioscHub can connect to your server (DioscHub connects to your server over the network).

Create a server with two MCP tools: one read tool and one state-changing tool. The standard MCP TypeScript SDK is @modelcontextprotocol/sdk.

On every tools/call, DioscHub forwards the signed-in user’s auth as HTTP request headers on the call. Read them the way you would on any inbound HTTP request, and authorize against your own system. DioscHub does not interpret them. (The JSON-RPC _meta field carries only non-credential context — the user and session ids, the current page — never the credential.)

server.ts
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import { z } from 'zod';
const server = new McpServer({ name: 'orders', version: '1.0.0' });
// Pull the forwarded user auth off the incoming HTTP request headers.
function userAuth(extra: { requestInfo?: { headers: Record<string, string | string[] | undefined> } }) {
const headers = extra.requestInfo?.headers ?? {};
const token = headers['authorization'];
if (!token) {
throw new Error('Unauthorized: no forwarded user credential');
}
return { token, cookie: headers['cookie'] };
}
// Read tool.
server.tool(
'get_order',
'Look up an order by id for the signed-in user.',
{ orderId: z.string() },
async ({ orderId }, extra) => {
const { token } = userAuth(extra);
const order = await myApi.getOrder(orderId, { token }); // authorize as the user
return { content: [{ type: 'text', text: JSON.stringify(order) }] };
},
);
// State-changing tool.
server.tool(
'cancel_order',
'Cancel an order for the signed-in user.',
{ orderId: z.string(), reason: z.string().optional() },
async ({ orderId, reason }, extra) => {
const { token } = userAuth(extra);
const result = await myApi.cancelOrder(orderId, reason, { token }); // authorize as the user
return { content: [{ type: 'text', text: JSON.stringify(result) }] };
},
);

Expose server over a Streamable HTTP transport — the transport DioscHub connects to. Point it at a public route such as https://your-mcp-server.example.com/mcp so DioscHub can reach it.

In the admin portal, create an MCP instance (a registered server) with:

  • Name: a globally-unique identifier, kebab-case (for example orders). Pick a name that stays unique.
  • Transport Type: Streamable HTTP. This is the supported transport.
  • Server URL: the reachable URL for your server.
  • Auth config: optional, static service-to-service credentials your server requires from every caller (for example an OAuth 2.1 client or fixed headers). This is separate from the per-user auth in step 4.

DioscHub loads your server’s tool list via the standard MCP tools/list operation and invokes tools via tools/call.

Your server reports each tool by its bare name (get_order, cancel_order). Inside DioscHub, each tool is bound under a qualified name:

<mcpInstanceName>_<bareToolName>

A single underscore joins the two segments. For an instance named orders, the tools become orders_get_order and orders_cancel_order. Use the qualified name wherever DioscHub asks which tools an Assistant or role may call. Per-instance configuration (such as approval rules) is keyed on the bare name, since it is already scoped to your one instance.

On every tools/call, DioscHub forwards the signed-in user’s auth headers and cookies opaquely to your server as HTTP request headers, unmodified (cookies folded into a single Cookie header). Your server reads them and authorizes the request as that user. DioscHub never interprets the credential. The credential never enters the model’s context (Credential Blind), so the model cannot see, store, or manipulate it. Your server is the authorization boundary: a tool acts with exactly the user’s permissions. For the full contract, see Forward the user’s auth.

5. Gate the state-changing tool behind consensus

Section titled “5. Gate the state-changing tool behind consensus”

A state-changing tool such as cancel_order can be placed behind an approval flow (consensus): the user reviews and explicitly approves the call before it runs. Configure approval per tool in the admin portal. See Gate state-changing tools behind Consensus for the full setup.

  1. Assign your tools to an Assistant (by their qualified names) in the admin portal.
  2. Open a Session with that Assistant.
  3. Ask the Assistant to do something your tool covers, for example “Look up order 1234” or “Cancel order 1234”.

The Assistant calls orders_get_order or orders_cancel_order, your server receives the forwarded user auth, and it authorizes the request as the signed-in user. If the state-changing tool is gated, the user sees an approval prompt before it runs.