Petstore walkthrough: from declaration to a selected runtime

This walkthrough uses one read-only Petstore catalog tool to show the published @theorvane/type-mcp@0.2.2 (opens in a new tab) flow: declare a server, inspect or compile it, then select the smallest supported runtime boundary.

What this does not do: TypeMCP does not choose hosting, authorization, persistence, models, LangGraph composition, or deployment. Those decisions remain in the application.

Before you start

Workspace checkpoint

For a project-starting route, complete Petstore project setup and Petstore TypeMCP foundation first. This walkthrough is the continuation that selects one runtime boundary after the declaration, explicit resolver, and local stdio path already compile.

Install

Install the package and Zod:

npm install @theorvane/type-mcp@0.2.2 zod

Use a Node-aware TypeScript configuration:

{
  "compilerOptions": {
    "target": "ES2022",
    "module": "NodeNext",
    "moduleResolution": "NodeNext",
    "lib": ["ES2022", "ESNext.Decorators", "DOM", "DOM.Iterable"],
    "strict": true,
    "verbatimModuleSyntax": true
  }
}

1. Declare the Petstore server

Create src/petstore-server.ts:

import { z } from "zod";
import { McpServer, McpTool } from "@theorvane/type-mcp";

@McpServer({ name: "petstore", version: "1.0.0" })
export class PetstoreServer {
  @McpTool({
    name: "find-product",
    description: "Find a Petstore product by SKU.",
    input: z.object({ sku: z.string().min(1) }),
  })
  findProduct({ sku }: { readonly sku: string }) {
    return { sku, available: true };
  }
}

The decorated class must keep a zero-argument constructor under the published 0.2.2 @McpServer contract. Configure a real catalog service through the explicit resolver before it creates PetstoreServer; TypeMCP does not construct or authorize that dependency for you.

2. Inspect, then compile through an explicit resolver

Create src/server.ts:

import {
  createMcpServer,
  getMcpServerDefinition,
  type InstanceResolver,
} from "@theorvane/type-mcp";
import { PetstoreServer } from "./petstore-server.js";

const definition = getMcpServerDefinition(PetstoreServer);
if (definition === undefined) {
  throw new Error("PetstoreServer is missing its declaration.");
}

const resolver: InstanceResolver<PetstoreServer> = {
  resolve: () => new PetstoreServer(),
};

export const server = await createMcpServer(PetstoreServer, resolver);

At this point, TypeMCP has validated the declaration, resolved one application instance, and compiled an MCP SDK server. The application still decides how requests reach that server and what policy applies to each call.

3. Select one boundary

Run locally over stdio

When an MCP-capable desktop or local client launches your process, add src/stdio.ts:

import { startStdioServer } from "@theorvane/type-mcp";
import { server } from "./server.js";

await startStdioServer(server);

TypeMCP connects the compiled server to the SDK stdio transport. Your application owns executable packaging, environment validation, process lifecycle, and access control. Continue with runtime selection.

Serve Streamable HTTP from a Fetch or Next.js host

Install no extra TypeMCP package; import the published HTTP subpath. Create src/mcp-handler.ts:

import { createMcpServer } from "@theorvane/type-mcp";
import { createMcpHandler } from "@theorvane/type-mcp/http";
import { PetstoreServer } from "./petstore-server.js";

export const handler = createMcpHandler(() =>
  createMcpServer(PetstoreServer, { resolve: () => new PetstoreServer() }),
);

A Fetch host can call handler(request). In a Next.js route, re-export it for GET, POST, and DELETE. The adapter owns JSON-RPC framing, protocol negotiation, and in-process MCP session routing. The host owns the URL, authentication, origin controls, durable-session policy, telemetry, and deployment. See HTTP framework integration and the executable standalone HTTP example (opens in a new tab).

Reuse the tool with LangChain

Install the optional peer only when you select this path:

npm install @theorvane/type-mcp@0.2.2 @langchain/core zod

Create src/langchain-tools.ts:

import { createLangChainTools } from "@theorvane/type-mcp/langchain";
import { PetstoreServer } from "./petstore-server.js";

export const tools = await createLangChainTools(PetstoreServer, {
  resolver: { resolve: () => new PetstoreServer() },
});

The adapter creates LangChain structured tools from decorated @McpTool methods. It does not start an MCP transport, build an agent, choose a model, or create a LangGraph graph. Pass tools to your own LangChain or LangGraph composition and retain the policy/state decisions there. See LangChain and LangGraph integration and the in-memory ToolNode example (opens in a new tab).

Run and verify

Choose exactly one continuation and run the matching check from the project root:

# stdio: compile first, then start the process; it remains attached to stdin/stdout.
npm run check
npm run inspect-server
npm run stdio

# HTTP: create src/mcp-handler.ts as shown above, then compile the consumer project.
npm run check

# LangChain: create src/langchain-tools.ts as shown above, then compile the consumer project.
npm run check

npm run check is the consumer-project verification: it typechecks each selected source file against the installed published package. For stdio, a successful process stays open for its MCP client rather than printing a completion line; stop it with Ctrl+C after the client has connected. HTTP and LangChain compilation does not deploy a listener or call a model.

Expected behavior

The declaration produces the public find-product tool name. The selected stdio path connects an already compiled server to a local process, the HTTP path returns a Fetch-compatible handler, and the LangChain path returns structured tools. None of these choices creates a host, authorizes a caller, selects a model, or persists application state.

Verify the pattern

Repository maintainers only: the following commands require this repository checkout (they are not commands for a copied consumer workspace). They prove the published HTTP and LangChain boundaries without a live listener, model, credential, or public Petstore request:

npm test -- --run test/standalone-http-example.test.ts
npm test -- --run test/langgraph-tool-node.test.ts

These smoke tests exercise the existing catalog example. In your application, add focused tests for the resolver, the tool's domain result, and the authorization policy that TypeMCP deliberately leaves to you.

Failure guide

Responsibility boundary

TypeMCP can validate the declaration, compile it through the resolver, and connect a compiled server to a selected published boundary. The application owns its catalog client, authorization, policy, process and host lifecycle, persistence, models, LangGraph composition, telemetry, and deployment.

Next steps