> ## Documentation Index
> Fetch the complete documentation index at: https://docs.usecontextlayer.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Expose MCP tools

> Author MCP tools and Views in a slate and connect an MCP client to the hosted slate's /mcp endpoint.

Add an `echo` tool to a slate, deploy it, and call it from a Model Context Protocol (MCP) client. The tool accepts a string and returns it as text. You can optionally attach a View that renders inside an MCP Apps-compatible client.

Start from an existing slate checkout. Each tool lives in a directory under `mcp/`, and the hosted slate serves its tools at `/mcp`. The local development server does not serve that endpoint, so you will test the tool after deployment.

> [!NOTE]
>
> MCP tools cannot use resources yet. An MCP declaration's `connections`, `databases` and `secrets` must be empty, and the build fails otherwise. Resource execution is implemented only for the app and agents.

## Add a tool

### 1. Create the tool's declaration

Create `mcp/echo/` and add a declaration next to the tool's code. It uses the same schema as the app's `public/slate.json`. This tool requires no resources:

```json mcp/echo/slate.json theme={null}
{ "connections": {}, "databases": {}, "secrets": {} }
```

### 2. Write the tool

`mcp/<name>/index.ts` exports two things. `tool` is the native MCP tool definition, with a Zod `inputSchema` and an optional Zod `outputSchema`. `handler` runs each call and returns an MCP tool result.

```ts mcp/echo/index.ts theme={null}
import { z } from "zod"

export const tool = {
  name: "echo",
  description: "Return the value it receives.",
  inputSchema: z.object({ value: z.string() }),
}

export function handler(args: unknown) {
  const { value } = args as z.infer<typeof tool.inputSchema>
  return { content: [{ type: "text" as const, text: value }] }
}
```

The slate's Worker validates the arguments against `inputSchema` before it calls `handler`. The starter's `workers/app.ts` already passes every `mcp/*/index.ts` module to `createSlateWorker`; keep that wiring.

### 3. Build and deploy

`pnpm build` runs the MCP build along with the app's, so it reports a declaration or definition error before you push. Commit and push `main` to deploy; see [Deploy](https://docs.usecontextlayer.com/how-to/deploy.md).

## Connect an MCP client

The slate's MCP endpoint is `https://<name>.contextlayer.xyz/mcp`. Replace `<name>` with the hosted slate name printed by `init`. ContextLayer registers this endpoint as an OAuth-protected resource when it creates the slate; you do not need to create a separate OAuth application for it.

### 1. Add the endpoint to your client

Add `https://<name>.contextlayer.xyz/mcp` to your MCP client as a remote server.

### 2. Sign in and approve the client

Follow the client's OAuth sign-in flow, sign in with your ContextLayer account, and approve access. If a coding agent is setting up the client, a person completes this browser approval.

The client discovers the authorization server through the slate's [OAuth resource metadata](https://docs.usecontextlayer.com/api-reference/slate-routing/get-oauth-metadata-for-a-slate-mcp-endpoint.md). ContextLayer identifies clients through their Client ID Metadata Document; this discovery is handled by the client.

### 3. Use the tools

Confirm that the client lists `echo`, then call it with:

```json theme={null}
{ "value": "Hello from my slate" }
```

The result should contain the text `Hello from my slate`. If you attached a View and the client supports MCP Apps, it can also display that View.

The client's token is issued for this slate's `/mcp` endpoint with the `mcp:access` scope. It does not authorize another slate or the platform management API.

Every MCP connection signs in; a browser session does not authenticate `/mcp`. The slate's viewing rule still applies: a private slate admits its owning user and members of its owning organization.

## Attach a View, if the tool needs one

The text-only tool above is complete without a View. To display an interface inside a compatible MCP client, add an HTML entry point at `mcp-ui/echo/index.html`:

```html mcp-ui/echo/index.html theme={null}
<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <title>Echo</title>
  </head>
  <body>
    <h1>Echo</h1>
    <p>This View is served by the slate. The tool returns its text in the client conversation.</p>
  </body>
</html>
```

Then add this property to the exported `tool` definition:

```ts theme={null}
_meta: { ui: { resourceUri: "ui://echo/index.html" } }
```

The resource URI identifies the matching HTML entry point. Several tools can reference the same View. Build and deploy again after adding or changing it.

This minimal View displays static HTML. To display inputs or results, or request another tool call, use the client's MCP Apps bridge from the View. The client supplies the tool data through that bridge; it does not give the View ContextLayer credentials or the app's browser session. See [MCP Views](https://docs.usecontextlayer.com/core-concepts/slate/mcp.md#views) for the relationship between a tool, its resource URI, and its View.

## What the SDK builds

During the build, the SDK's Vite integration evaluates each `mcp/<name>/index.ts`, validates its `slate.json`, and converts its Zod schemas to JSON Schema. It writes the tools and Views into a generated `/mcp.json` manifest among the slate's assets. It compiles each View into one HTML file with its scripts and styles inlined.

ContextLayer reads the deployed manifest to list the slate's tools and serve its Views. Tool discovery does not import your authored modules. Each actual tool call runs your `handler` in the slate's Worker.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.