# How clients are named
Source: https://docs.alpic.ai/analytics/clients
How Alpic groups the MCP clients that connect to your server, and what the Unnamed client row means.
Every chart in Analytics can be split by client. The client name comes from the MCP handshake: when a client connects, it announces itself with a name of its own choosing. Nobody standardizes those names, so Alpic maps them to a canonical list before showing them to you.
### Why the raw names need mapping
Three things happen with raw client names, and all three make a breakdown harder to read than it needs to be.
* **Vendors rename their clients.** Claude.ai used to announce itself as `claude-ai` and now sends `Anthropic/ClaudeAI`. Codex moved from `codex-mcp-client` to `openai-mcp (Codex)`, and that switch is still in progress, so its traffic is split across both names right now. Older builds keep sending the old name for months, so without mapping the same product shows up as two rows forever.
* **Proxies add their own version to the name.** A client reached through `mcp-remote` announces itself as `claude-code (via mcp-remote 0.1.37)`, so one client turns into one row per release of the proxy.
* **Clients that never set a name still send one.** The Python MCP SDK defaults `clientInfo.name` to `mcp`, so a large share of traffic arrives under a name that means nothing.
### What Alpic does
Alpic keeps a list of the clients it recognizes and folds every name a vendor has shipped for the same product into one row. `claude-ai` and `Anthropic/ClaudeAI` both count as **Claude**. `codex-mcp-client` and `openai-mcp (Codex)` both count as **Codex**. Proxy versions are stripped before the lookup, so all `mcp-remote` variants of a client land on that client.
Surfaces stay separate. Claude Code, Claude, Claude Cowork and the Anthropic API each get their own row under the same vendor logo, and so do ChatGPT, Codex and the Responses API under OpenAI's. They are different ways of using your server, so you probably want to tell them apart.
A name Alpic does not recognize is left alone. It gets its own row, spelled exactly as it was received, without a logo. Alpic will not guess at what it is, and neither should you read too much into it: on a private server these are usually your own agents, scripts and uptime probes, but on a public server anyone can connect under any name they like.
One row deserves an explanation: **Unnamed client** is traffic that sent no name at all, so all Alpic received was the SDK default. There is nothing more to say about it, it could be any client or script.
### What to keep in mind when reading the breakdown
* **The client breakdown counts connections, not engagement.** A session is one MCP handshake, and every client reconnects on its own schedule. Claude Code handshakes when a conversation starts and again whenever it considers what it knows about your server stale, whether or not it ends up calling a tool, so it racks up handshakes with very few tool calls behind them. A client like the Alpic Playground is the other way round, several tool calls per handshake. A tall bar means a client connects a lot, not that people are using your server through it a lot. Look at tool calls for that.
* **OpenAI surfaces cannot always be told apart.** Every OpenAI surface used the same `openai-mcp` name before they started adding a suffix, so ChatGPT, the Responses API and Agent Builder are mixed together in the **OpenAI Other** row. A number labelled ChatGPT is therefore a floor, not a total. Vendor-level OpenAI totals are reliable.
* **Client names are self-reported.** Anything can claim any name in the handshake. Alpic shows the mapped name for readability and nothing more: it is never used to authenticate or authorize a request, and the vendor logos are decoration, not verification.
* **Raw names are always available.** Filtering and the session list use the exact name received, so you can still search for `claude-ai` or your own agent name and find those sessions.
# Custom Events
Source: https://docs.alpic.ai/analytics/custom-events
Record your own events and user traits from inside your MCP server's tool handlers.
### Overview
Alpic tracks every tool call automatically, but only your server knows what happened inside one: which backend was hit, how many results came back, whether a payment went through. Custom events let you record those moments from inside your tool handlers. Each event is attached to the current session, interleaved with the tool calls on its timeline.
You can also attach traits to the user behind the request (an email, a name, a plan) with `identify`.
Events are buffered while your handler runs and shipped through private metadata configured by the Alpic runtime, so there is no extra network call. Alpic ingests and strips them at the proxy before the response reaches the MCP client. Outside Alpic, no analytics metadata is added to the response.
To capture what happens in the UI your tools render, see [View events](/analytics/view-events).
### 1. Install the package
```bash theme={null}
pnpm add @alpic-ai/insights
```
### 2. Wire it into your server
The package ships two entry points depending on which MCP framework you're using.
Use `analyticsMiddleware`: it returns a Skybridge `McpMiddlewareFn` you register via `mcpMiddleware()`. Add it
before your tool/widget registrations:
```typescript theme={null}
import { analyticsMiddleware } from "@alpic-ai/insights";
import { McpServer } from "skybridge/server";
const server = new McpServer({ name: "my-mcp-server", version: "1.0.0" }, { capabilities: {} })
.mcpMiddleware(analyticsMiddleware())
.registerTool(/* ... */);
```
Use `track`, it accepts an `McpServer` (or a low-level `Server`) and patches the `tools/call` handlers in place.
It can be called before or after your tools are registered:
```typescript theme={null}
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { track } from "@alpic-ai/insights";
const server = new McpServer(
{ name: "my-mcp-server", version: "1.0.0" },
{ capabilities: {} },
);
server.registerTool(/* ... */);
track(server);
```
### 3. Capture events from your tool handlers
Once wired in, every tool handler receives an `analytics` object on its `extra` argument. Cast `extra` with the
exported `AnalyticsExtra` type to get typed access:
```typescript theme={null}
import type { AnalyticsExtra } from "@alpic-ai/insights";
server.registerTool("search_flights", {/* ... */}, async ({ from, to }, extra) => {
const { analytics } = extra as unknown as AnalyticsExtra;
const startedAt = performance.now();
const flights = await flightApi.search(from, to);
analytics.capture("flights_searched", {
duration: performance.now() - startedAt,
properties: { from, to, resultCount: flights.length },
});
return { content: [/* ... */] };
});
```
`capture(name, options)` takes the event name plus optional details:
| Option | Type | Description |
| ------------ | ------------------------- | -------------------------------------------------------------------------------------- |
| `message` | `string` | A human-readable line shown with the event. Max 500 characters. |
| `duration` | `number` | How long the operation took, in milliseconds. Fractional values are fine. |
| `properties` | `Record` | Freeform structured detail. Max 8 KB serialized. |
| `isError` | `boolean` | Marks the event as an error on the timeline. |
| `error` | `string` | An error description. Setting it also marks the event as an error. Max 500 characters. |
### 4. Identify the user
`identify` attaches string traits to the user making the current request. Alpic resolves who that is from the
request's auth context (see how users are counted), so you
don't pass any id:
```typescript theme={null}
analytics.identify({
email: user.email,
plan: user.plan,
});
```
Each `identify` call is a complete snapshot. Calling it several times in one request keeps only the last snapshot, and
the next request's call replaces the traits stored for that user.
After Alpic has observed user-unique traits, choose one under **Project settings → Privacy → Primary user identity**.
That environment-wide choice is shown as the main user label in analytics while Alpic keeps the stable internal user
ID for grouping and navigation.
Traits are user data. They are only stored on a paid plan with **Collect user data** enabled for the environment, like
the rest of the
privacy settings
. On the Free plan or with the setting off, `identify` calls are dropped.
### 5. Deploy
Deploy on Alpic as usual. Once the new version is live, events captured by your handlers start flowing into your
project's analytics.
### Run locally or on another host
Pass a handler to receive each request's analytics batch yourself. The same option works with `analyticsMiddleware`:
```typescript theme={null}
track(server, {
handler: async ({ events, traits }) => {
await myAnalytics.write({ events, traits });
},
});
```
The handler also runs on Alpic, in addition to dashboard ingestion. Handler failures are logged and never fail the
tool call.
### Good to know
* Only `tools/call` requests carry an `analytics` object; other request types pass through untouched.
* Call `capture` and `identify` before your handler returns. Calls made after the response is sent (for example from
a detached promise) are dropped, with a warning outside production.
* At most 50 events are kept per tool call; extra ones are dropped.
* Event names are capped at 200 characters.
* Event timestamps are set when `capture` is called. Timestamps more than 24 hours old, or in the future, are
replaced by the time Alpic received the event.
* The middleware never throws into your handler: if analytics can't be attached, your tool result is returned as-is.
# Metric definitions
Source: https://docs.alpic.ai/analytics/metrics
Exactly what each analytics metric measures.
Every metric on the analytics dashboard is computed from the MCP requests Alpic records as they reach your server. This page defines each one precisely, so you always know what you are looking at.
### Sessions and conversations
Two concepts matter before reading any session number, because they are easy to confuse:
**A session is an MCP protocol session.** When an MCP client connects to your server, it sends an initialize request that starts a new session. This handshake announces the client's name and capabilities, which is how Alpic knows whether traffic comes from ChatGPT, Claude, Cursor, or another client. The session then carries every request from that client until the connection ends or expires.
**A conversation is a chat thread in the end user's client.** The MCP protocol itself has no notion of a conversation: your server never sees the chat. Some clients choose to share a conversation identifier with each request. Today only ChatGPT does this, so for ChatGPT traffic Alpic knows which chat thread each session belongs to.
The two do not map one to one. A single conversation can create several MCP sessions: clients reconnect, users return to an older chat, sessions expire and new ones open. This is normal protocol behavior, not a sign of anything wrong.
| | ChatGPT | Other clients (Claude, Cursor, ...) |
| --------------------------------- | ------- | ----------------------------------- |
| MCP session | Yes | Yes |
| Conversation identifier | Yes | No |
| Sessions grouped per conversation | Yes | No, each session stands alone |
How it's measured:
* The **Sessions** metric counts `initialize` requests received from recognized MCP clients over the selected period. Think of it as how many times your app was opened or reconnected to. See [How clients are named](/analytics/clients) for how each client is identified in the breakdowns.
* Session-level metrics (**Sessions with errors** and **Session error rate**) group by conversation when the client provides one, and by MCP session otherwise. For ChatGPT traffic this means a conversation counts once, even if it opened several sessions along the way.
Because one conversation can span several sessions, the Sessions count for ChatGPT traffic can be higher than the
number of actual conversations. That gap is expected.
### Requests
A request is any MCP request received by your server: tool calls, but also protocol traffic such as `initialize`, `tools/list`, `prompts/get`, or `resources/read`. Request charts can be scoped to tool calls only, to protocol requests only (excluding 'initialize'), or to everything.
Protocol requests don't necessarily reflect active usage. MCP clients send them frequently just by being configured to
connect to your server, so look at tool calls when you want to measure real engagement.
### Tool calls
The number of `tools/call` requests received. This is the best proxy for actual usage of your server: each tool call means an LLM decided to use one of your tools.
The tool performance table breaks this down per tool, with:
* **Requests**: the number of calls to that tool
* **p50 / p95 latency**: median and 95th percentile response time
* **Success rate**: the share of calls that completed without any error
### Users
The number of distinct identified people who used your server over the period. This metric has enough subtlety to deserve its own page: see How users are counted.
### Output tokens
An estimate of how many tokens your server's responses add to the LLM's context window, shown as an average per call. Alpic estimates the token equivalent of each response part that gets passed to the model: tool call results, tool lists and descriptions, prompt contents, resource contents, and server instructions sent during `initialize`.
Use this to spot tools that flood the context window with oversized responses.
These are estimates of context usage, not billed tokens from any LLM provider.
### Latency
The time between a request reaching Alpic and your server's response, measured at the platform level. Charts show p50, p95, and p99 percentiles rather than averages, so slow outliers stay visible.
### Errors
Alpic separates two kinds of errors, because they mean very different things:
* **Tool errors**: a tool call returned a result with `isError` set to true. These are common and by design: the error is passed back to the LLM so it can recover, retry, or rephrase. A baseline of tool errors is normal. If you want to design recoverable errors well, check out this article.
* **MCP errors**: protocol-level errors that are not passed to the LLM and usually surface as error messages in the user's client. These deserve your attention. Check your logs if you see a spike.
MCP errors are further classified by type:
| Type | Meaning |
| ------------ | --------------------------------------------------- |
| Timeout | The request timed out before your server answered |
| Missing tool | The client called a tool your server doesn't expose |
| Protocol | Malformed or invalid request |
| Connection | The connection to your server failed |
| Server error | Your server returned an unexpected error |
Protocol implementation quality varies across MCP clients and can generate more errors than expected. For instance,
Claude requests the list of resources and prompts even when your server doesn't advertise any, which produces MCP
errors you can safely ignore.
### Session error rate
The share of sessions that contained at least one error, whether a tool error or an MCP error. As explained [above](#sessions-and-conversations), sessions are grouped by conversation when the client provides one. This is a good health metric to watch over time: a rising session error rate means a growing share of your users are hitting problems.
# Overview
Source: https://docs.alpic.ai/analytics/overview
Understand how your MCP server is used, out of the box.
### Overview
Alpic Analytics gives you a complete picture of how your MCP server is used: how many sessions and users you have, which tools are called, how fast they respond, and where errors happen.
There is nothing to install. Alpic sits in front of your MCP server and records every MCP request as it passes through: the method called, the MCP client, the response time, the outcome, and the user identity when one is available. Your code stays untouched and no SDK is required.
Analytics are collected separately for each environment. The dashboard defaults to your production environment; use
the environment picker to switch.
### Key indicators
The top of the dashboard shows five key indicators for the selected period, each compared to the previous period of the same length:
* **Sessions**: how many times your app was opened
* **Tool calls**: how many `tools/call` requests users, agents and an LLM intentionally invoked
* **Users**: how many distinct identified people used your server
* **Sessions with errors**: how many sessions contained at least one error
* **Session error rate**: the share of sessions that contained an error
Every metric has a precise definition. See [Metric definitions](/analytics/metrics) for exactly what each one measures, and [How users are counted](/analytics/unique-users) for the full story on user identification.
### Sessions
The sessions chart shows sessions over time, broken down per MCP client (ChatGPT, Claude, Cursor, and so on). Below it, a daily activity calendar shows when your app is used over the past year, switchable between request volume, latency, and errors, so you can spot usage patterns and growth at a glance.
### Users
The users chart shows unique users over time, broken down per MCP client. When your server has identified users, a Top users card ranks the most active ones.
### Tools
The tool performance table lists every tool with its request count, p50 and p95 latency, and success rate. Select one or more tools to focus the charts below on them:
* **Requests** over time
* **Latency** over time (p50, p95, p99)
* **Tokens** per call
Each chart can be scoped to tool calls only, to protocol requests only (tools/list, prompts/get, and resources/read; excluding 'initialize' requests), or to everything (including `initialize` requests).
### Reliability
The reliability section tracks errors over time, split between tool errors and MCP errors, with breakdowns by tool and by error type. See [Metric definitions](/analytics/metrics#errors) for what each error type means.
### Time ranges and granularity
You can view analytics over the last day, week, month, or year, or pick a custom date range. Chart granularity adjusts automatically to the range you select, from 5 minute buckets on short ranges up to daily buckets on long ones.
How far back you can look depends on your plan's analytics retention, from 7 days on the Free plan to unlimited on Enterprise. Time ranges beyond your retention appear locked in the picker. See [Plans](/pricing/plans) for details.
### Go further
Exactly what each metric measures
Identity sources and unique user counting
Control what data is collected
# Privacy & Data Collection
Source: https://docs.alpic.ai/analytics/privacy
Control what data Alpic collects about your MCP server's traffic.
Alpic is conservative by default: analytics work out of the box on metadata alone, and anything that could contain end-user content is opt-in.
### What is collected by default
For every MCP request, Alpic records metadata only:
* The method and tool name called
* The MCP client name (ChatGPT, Claude, Cursor, ...)
* Timestamp, response time, and estimated output tokens
* The error type and code, if the request failed
* A user identity when one can be resolved, stored as a hash for keys and tokens (see How users are counted)
What is never collected, on any plan:
* **IP addresses**
* **Raw API keys or tokens**, only hashes are stored
* **Request or response contents**, unless you explicitly enable payload collection below
### Data collection settings
Data collection is configured in **Project Settings → Privacy**, separately for each environment. This lets you, for example, collect payloads on staging while keeping production metadata-only.
| Setting | Default | What it does |
| ----------------------------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Session replay** | Off | Captures sessions so they can be replayed. Required before payload collection can be enabled. |
| **Collect request payloads** | Off | Stores request bodies so they can be inspected in session replay. |
| **Collect response payloads** | Off | Stores response bodies so they can be inspected in session replay. |
| **Collect error messages** | On | Stores error messages alongside analytics events. May echo end-user input. |
| **Collect user data** | Off | Stores the authenticated end user's email (from their OIDC token, when present), so sessions show who made each request, plus the coarse location ChatGPT reports alongside the call. |
All of these settings require a paid plan. On the Free plan they appear locked, and nothing beyond metadata is ever stored, including error messages.
Payloads and error messages may contain end-user PII. You are responsible for what your server puts in them. Review
your obligations in our
Privacy Policy
before enabling collection.
A few practical notes:
* The coarse location is only what ChatGPT itself sends with the call: country, city, and coordinates rounded to two decimals (about 1.1 km). It is never derived from the caller's IP address, and calls from other MCP clients carry no location at all.
* Payloads are collected only for `tools/call`, `prompts/get`, and `resources/read` requests, and only while both the corresponding toggle and session replay are enabled.
* Payloads larger than 256 KB are skipped.
* Collected data follows your plan's analytics retention.
# How users are counted
Source: https://docs.alpic.ai/analytics/unique-users
Exactly how Alpic identifies unique users of your MCP server.
The **Users** metric counts distinct identified people. Each identity is counted once over the selected period, no matter how many sessions it opened or requests it made. This page explains where identities come from, so you can trust the number and know its limits.
### Where identity comes from
On every request, Alpic tries to resolve who is calling. Four identity sources exist, checked in this order, and the first one that applies wins:
1. **OAuth identity**: if your server uses OAuth authentication, the user is identified by the subject of their access token. This is the strongest signal: one entry per authenticated person.
2. **Client-provided subject**: ChatGPT attaches a stable, anonymized identifier for the end user to each request. Alpic uses it when no OAuth identity is present, so ChatGPT users are counted individually even without OAuth.
3. **API key**: for requests authenticated with an `x-api-key` header, the identity is a SHA-256 hash of the key. The dashboard shows a short fingerprint of it, never the key itself.
4. **Bearer token**: for opaque bearer tokens that carry no user subject, the identity is a hash of the token.
If none of these apply, the request is **anonymous**.
### What this means in practice
* **Anonymous traffic is never counted as users.** Alpic does not guess identities from IP addresses, does not fingerprint clients, and does not create synthetic anonymous users. In fact, Alpic never collects IP addresses at all. This is why you won’t see anonymous users from Claude, Cursor, or other clients that do not provide a subject identifier - currently, only ChatGPT provides one. If your Users number looks low compared to Sessions, it usually means most of your traffic is unauthenticated.
* **A shared credential counts as one user.** An API key or token used by a whole team appears as a single user, since Alpic has no way to tell the individuals apart.
* **Raw credentials are never stored.** API keys and tokens are hashed before anything is written; only the hash is kept.
* **Large counts are approximate.** Beyond tens of thousands of users, counts use an approximation algorithm and are displayed with a `~` prefix. The error margin is well under 1%.
* **User metrics have a start date.** Unique users, session error rate, and the session list are new metrics that can't be reconstructed from older data. Charts show a "Tracking started" marker; there is no user data before it.
### Getting accurate user counts
The quality of your Users metric follows directly from how your server authenticates:
* **OAuth** gives you the most accurate picture: every user is counted individually, on every client. See OAuth authentication to set it up.
* **ChatGPT traffic** is counted per user even without OAuth, thanks to the client-provided subject.
* **API keys and bearer tokens** count credentials, not people. Fine if each key belongs to one person, misleading if keys are shared.
* **No authentication** means no user counting at all.
# View Events
Source: https://docs.alpic.ai/analytics/view-events
Capture events from inside your MCP App or ChatGPT App view with the React provider.
### Overview
[Custom events](/analytics/custom-events) let your server record what happens inside a tool call. View events are the other half: they capture what happens in the UI your tools render, the [MCP App / ChatGPT App view](https://modelcontextprotocol.io/extensions/apps/overview) shown in the client. The `@alpic-ai/insights/react` provider captures a baseline of view activity on its own, and lets you record your own events with a hook.
Both land on the same session timeline as your server-side events, so a session reads end to end: the tool call that opened the view, what the user did in it, and any error along the way.
There are no keys to configure. The provider discovers where to send events from the metadata Alpic stamps onto each tool result, and ships them best-effort, so a delivery failure never affects the host view.
### 1. Install the package
```bash theme={null}
pnpm add @alpic-ai/insights
```
The React entry point is shipped under `@alpic-ai/insights/react`. It expects `react` as a peer dependency, which your view already has.
### 2. Wrap your view
Render `AlpicAnalytics` once at the root of your view, above everything that captures events:
```tsx theme={null}
import { AlpicAnalytics } from "@alpic-ai/insights/react";
import { createRoot } from "react-dom/client";
import { App } from "./App";
createRoot(document.getElementById("root")!).render(
,
);
```
That is all the setup required. Events captured before the ingest endpoint is known are buffered and flushed automatically once it arrives.
### 3. Capture events
Call `useAnalytics()` from any component under the provider and use `capture` to record an event:
```tsx theme={null}
import { useAnalytics } from "@alpic-ai/insights/react";
function BookButton({ from, to }: { from: string; to: string }) {
const analytics = useAnalytics();
return (
);
}
```
`capture(name, options)` takes the event name plus optional details:
| Option | Type | Description |
| ------------ | ------------------------- | -------------------------------------------------------------------------------------- |
| `message` | `string` | A human-readable line shown with the event. Max 500 characters. |
| `duration` | `number` | How long the operation took, in milliseconds. Fractional values are fine. |
| `properties` | `Record` | Freeform structured detail. Max 8 KB serialized. |
| `isError` | `boolean` | Marks the event as an error on the timeline. |
| `error` | `string` | An error description. Setting it also marks the event as an error. Max 500 characters. |
Event names starting with `$` are reserved for the events the SDK captures itself (see below). Use plain names for
your own events.
### Auto-captured events
The provider captures a baseline of activity without any `capture` calls. Each surface is gated by the `autoCapture` prop:
| Surface | Default | Events |
| -------------- | ------- | ------------------------------------------------------------------------------------------------------------ |
| `lifecycle` | On | `$loaded` when the view mounts, `$visible` / `$hidden` as it is shown or hidden, `$closed` when it goes away |
| `errors` | On | `$error` for every uncaught error and unhandled promise rejection, with the message, stack, and source |
| `interactions` | Off | A click event for any element carrying a `data-alpic-event` attribute (see below) |
Toggle any surface with the prop. Lifecycle and errors are on by default; interactions are opt-in:
```tsx theme={null}
```
#### Declarative interaction capture
With `interactions` enabled, add `data-alpic-event` to any element to capture its clicks. The attribute value is the event name; every other `data-alpic-*` attribute becomes a string property:
```tsx theme={null}
```
A click on that button captures `cta_clicked` with `{ plan: "pro" }`. Capture is delegated from a single `document` listener and resolves the nearest matching ancestor, so it works for elements added after mount.
### Transform or drop events with `beforeSend`
Pass `beforeSend` to inspect, rewrite, or drop each event before it is buffered. Return the event to keep it, a modified event to change it, or `null` to drop it. It runs synchronously for every event, whether captured by you or by auto-capture:
```tsx theme={null}
{
if (!hasConsent) {
return null;
}
return { ...event, properties: { ...event.properties, appVersion } };
}}
>
```
`beforeSend` is read live on every event, so deriving it from state (a consent flag, say) takes effect without remounting the provider.
### Good to know
* The provider is client-only: it discovers the ingest endpoint from the tool-result metadata and needs no API key.
* Delivery is best-effort. Failures are swallowed so analytics never affects the view; events are flushed when the view is hidden or unloaded, and on a short interval otherwise.
* At most 100 events are buffered at a time; once full, further events are dropped with a single console warning.
* View events follow the same [privacy settings](/analytics/privacy) and [plan retention](/pricing/plans) as the rest of your analytics. Do not put end-user PII in event names or properties unless you intend to collect it.
# Using the REST API
Source: https://docs.alpic.ai/api-reference
Learn how to use Alpic's REST API to programmatically manage your MCP servers, teams, and projects
## Who is this API for?
Alpic's REST API is designed for developers who want to automate deployment workflows and manage MCP server projects programmatically. The API supports:
* **Teams** — list teams available to the authenticated user
* **Projects** — create, list, retrieve, update, and delete MCP server projects
* **Environments** — create and retrieve environments, deploy them, and fetch runtime logs
* **Environment Variables** — list, add, update, and delete environment variables per environment
* **Deployments** — upload artifacts, list deployments, inspect deployment details, and retrieve deployment logs
* **Analytics and Tunnels** — fetch project analytics and generate tunnel tickets
* **Distribution** — publish to the MCP Registry ([Publishing docs](/distribution/publishing))
* **Beacon** — check a MCP server readiness for chatGPT and Claude
Use the API to integrate Alpic into your development workflow, automate deployments, and operate your MCP servers end to end.
## Getting Started
### Get Your API Key
To use the REST API, you'll need an API key. Here's how to get one:
Navigate to [https://app.alpic.ai](https://app.alpic.ai) and sign in to your account.
Click on your team name in the top left corner of the screen, then select the **API Keys** tab on the left sidebar.
Click **New API key** and give it a name (e.g., "CI/CD Pipeline").
Copy the API key immediately—you won't be able to see it again. Store it securely in your environment variables or secrets manager.
Keep your API keys secure and never commit them to version control. Treat them like passwords—if exposed, revoke them
immediately and generate new ones.
## Alpic teams structure
Understanding the hierarchy of Alpic's resources is important when working with the API:
* **API Key**: Each API key is associated with a specific team. The API key grants access to resources within that team.
* **Team**: A team can contain multiple projects and is the top-level organizational unit.
* **Project**: Each project represents an MCP server and can have multiple environments.
* **Environment**: Environments represent different deployment environments (e.g., production, staging, development) for your MCP server, linked to a specific branch of your git repository. Learn more about [environments here](/build-deploy/environments).
```mermaid theme={null}
flowchart LR
A[API Key] <--> B[Team]
B --> C1[Project 1]
B --> C2[Project N]
C1 --> D1[Env: production]
C1 --> D2[Env: staging]
C2 --> D3[Env: production]
C2 --> D4[Env: dev]
```
When making API requests, you'll work with resources at the project and environment levels, all scoped to the team associated with your API key.
## Authentication
All API requests require authentication using your API key. Include it in the `Authorization` header as a Bearer token:
```bash theme={null}
Authorization: Bearer YOUR_API_KEY
```
## Base URL
All API requests should be made to:
```
https://api.alpic.ai
```
## API Versioning
The API uses versioning in the URL path. The current version is `v1`, so all endpoints are prefixed with `/v1/`.
Example:
```
https://api.alpic.ai/v1/projects
```
## Response Format
All API responses are returned in JSON format. Successful responses use standard HTTP status codes (200, 201, etc.), while errors return appropriate error codes (400, 401, 404, 500, etc.) with error details in the response body.
# Get project analytics
Source: https://docs.alpic.ai/api-reference/analytics/get-project-analytics
https://api.alpic.ai/openapi.json get /v1/analytics/{projectId}
Get analytics data for a project over a time range
# Create a beacon audit
Source: https://docs.alpic.ai/api-reference/beacon/create-a-beacon-audit
https://api.alpic.ai/openapi.json post /v1/beacon/audits
Audit an MCP server for spec compliance and AI client compatibility
# Get a beacon audit
Source: https://docs.alpic.ai/api-reference/beacon/get-a-beacon-audit
https://api.alpic.ai/openapi.json get /v1/beacon/audits/{auditId}
Get a beacon audit by ID, including the report if completed
# Get a deployment
Source: https://docs.alpic.ai/api-reference/deployments/get-a-deployment
https://api.alpic.ai/openapi.json get /v1/deployments/{deploymentId}
Get a deployment by ID
# Get deployment logs
Source: https://docs.alpic.ai/api-reference/deployments/get-deployment-logs
https://api.alpic.ai/openapi.json get /v1/deployments/{deploymentId}/logs
Get the logs for a deployment
# List project deployments
Source: https://docs.alpic.ai/api-reference/deployments/list-project-deployments
https://api.alpic.ai/openapi.json get /v1/projects/{projectId}/deployments
List all deployments for a project
# Get server info
Source: https://docs.alpic.ai/api-reference/distribution/get-server-info
https://api.alpic.ai/openapi.json get /v1/distribution/get
Get info about a server
# Publish a server to the MCP registry
Source: https://docs.alpic.ai/api-reference/distribution/publish-a-server-to-the-mcp-registry
https://api.alpic.ai/openapi.json post /v1/distribution/publish
# Add environment variables
Source: https://docs.alpic.ai/api-reference/environments/add-environment-variables
https://api.alpic.ai/openapi.json post /v1/environments/{environmentId}/environment-variables
Add one or more environment variables to an environment
# Create an environment
Source: https://docs.alpic.ai/api-reference/environments/create-an-environment
https://api.alpic.ai/openapi.json post /v1/environments
Create an environment for a project
# Delete an environment variable
Source: https://docs.alpic.ai/api-reference/environments/delete-an-environment-variable
https://api.alpic.ai/openapi.json delete /v1/environment-variables/{environmentVariableId}
Delete an environment variable by ID
# Deploy an environment
Source: https://docs.alpic.ai/api-reference/environments/deploy-an-environment
https://api.alpic.ai/openapi.json post /v1/environments/{environmentId}/deploy
Deploy an environment
# Get an environment
Source: https://docs.alpic.ai/api-reference/environments/get-an-environment
https://api.alpic.ai/openapi.json get /v1/environments/{environmentId}
Get an environment by ID
# Get latest logs
Source: https://docs.alpic.ai/api-reference/environments/get-latest-logs
https://api.alpic.ai/openapi.json get /v1/environments/{environmentId}/latest-logs
Get the N most recent logs for an environment
# Get logs
Source: https://docs.alpic.ai/api-reference/environments/get-logs
https://api.alpic.ai/openapi.json get /v1/environments/{environmentId}/logs
Get logs for an environment
# Get playground configuration
Source: https://docs.alpic.ai/api-reference/environments/get-playground-configuration
https://api.alpic.ai/openapi.json get /v1/environments/{environmentId}/playground
Get the playground configuration for an environment
# List environment variables
Source: https://docs.alpic.ai/api-reference/environments/list-environment-variables
https://api.alpic.ai/openapi.json get /v1/environments/{environmentId}/environment-variables
List all environment variables for an environment
# Update an environment variable
Source: https://docs.alpic.ai/api-reference/environments/update-an-environment-variable
https://api.alpic.ai/openapi.json patch /v1/environment-variables/{environmentVariableId}
Update an environment variable by ID
# Update playground configuration
Source: https://docs.alpic.ai/api-reference/environments/update-playground-configuration
https://api.alpic.ai/openapi.json put /v1/environments/{environmentId}/playground
Update the playground configuration for an environment. All fields are optional — only provided fields are updated.
# Create an intent category
Source: https://docs.alpic.ai/api-reference/insights/create-an-intent-category
https://api.alpic.ai/openapi.json post /v1/environments/{environmentId}/intent-categories
Create an intent category, optionally linking it to existing intents
# Delete an intent category
Source: https://docs.alpic.ai/api-reference/insights/delete-an-intent-category
https://api.alpic.ai/openapi.json delete /v1/environments/{environmentId}/intent-categories/{categoryId}
Delete an intent category; its intents are unlinked but not deleted
# Link an intent category
Source: https://docs.alpic.ai/api-reference/insights/link-an-intent-category
https://api.alpic.ai/openapi.json post /v1/environments/{environmentId}/intent-categories/{categoryId}/link
Link an intent category to intents; already linked intents are ignored
# List feedbacks
Source: https://docs.alpic.ai/api-reference/insights/list-feedbacks
https://api.alpic.ai/openapi.json get /v1/environments/{environmentId}/feedbacks
List user feedback for an environment over a time range
# List intent categories
Source: https://docs.alpic.ai/api-reference/insights/list-intent-categories
https://api.alpic.ai/openapi.json get /v1/environments/{environmentId}/intent-categories
List intent categories for an environment, including how many intents each category is linked to
# List intents
Source: https://docs.alpic.ai/api-reference/insights/list-intents
https://api.alpic.ai/openapi.json get /v1/environments/{environmentId}/intents
List grouped user intents for an environment over a time range
# Unlink an intent category
Source: https://docs.alpic.ai/api-reference/insights/unlink-an-intent-category
https://api.alpic.ai/openapi.json post /v1/environments/{environmentId}/intent-categories/{categoryId}/unlink
Unlink an intent category from intents; intents without the category are ignored
# Update an intent category
Source: https://docs.alpic.ai/api-reference/insights/update-an-intent-category
https://api.alpic.ai/openapi.json patch /v1/environments/{environmentId}/intent-categories/{categoryId}
Rename or recolor an intent category
# Create a project
Source: https://docs.alpic.ai/api-reference/projects/create-a-project
https://api.alpic.ai/openapi.json post /v1/projects
Create a project for a team
# Delete a project
Source: https://docs.alpic.ai/api-reference/projects/delete-a-project
https://api.alpic.ai/openapi.json delete /v1/projects/{projectId}
Delete a project and all its environments
# Get a project
Source: https://docs.alpic.ai/api-reference/projects/get-a-project
https://api.alpic.ai/openapi.json get /v1/projects/{projectId}
Get a project by ID
# List projects
Source: https://docs.alpic.ai/api-reference/projects/list-projects
https://api.alpic.ai/openapi.json get /v1/projects
List all projects for a team
# Update a project
Source: https://docs.alpic.ai/api-reference/projects/update-a-project
https://api.alpic.ai/openapi.json patch /v1/projects/{projectId}
Update project settings
# Get team usage
Source: https://docs.alpic.ai/api-reference/teams/get-team-usage
https://api.alpic.ai/openapi.json get /v1/teams/{teamId}/usage
Get the MCP request usage of a team for its current billing window. A team that has made no request in the window reports zero.
# List teams
Source: https://docs.alpic.ai/api-reference/teams/list-teams
https://api.alpic.ai/openapi.json get /v1/teams
List all teams for the authenticated user
# Get a tunnel ticket
Source: https://docs.alpic.ai/api-reference/tunnels/get-a-tunnel-ticket
https://api.alpic.ai/openapi.json get /v1/tunnels/ticket
Get a signed ticket for establishing a tunnel connection. Requires user authentication (API keys are not supported).
# Hosting Assets
Source: https://docs.alpic.ai/build-deploy/assets
Learn how to host complete ChatGPT Apps on Alpic
## Overview
[ChatGPT Apps](https://openai.com/index/introducing-apps-in-chatgpt/) are a new way for ChatGPT users to interact with your services directly from the ChatGPT interface. In practice, ChatGPT Apps are MCP servers exposing tools and MCP resources linking to UI components.
Alpic allows you to host complete ChatGPT Apps with zero-configuration, by hosting both the MCP server and the UI assets used by your ChatGPT App.
To learn more in detail about how OpenAI ChatGPT Apps work, read our blog post: [Inside OpenAI’s Apps SDK: How to
Build Interactive ChatGPT Apps with
MCP](https://alpic.ai/blog/inside-openai-s-apps-sdk-how-to-build-interactive-chatgpt-apps-with-mcp).
## Building your ChatGPT App
Alpic provides a minimal starter kit in TypeScript to help you bootstrap your ChatGPT App.
To get started, fork the [Alpic Apps SDK starter kit](https://github.com/alpic-ai/apps-sdk-template) or simply use the following link to clone the repository and deploy it on Alpic:
[](https://app.alpic.ai/new/clone?repositoryUrl=https%3A%2F%2Fgithub.com%2Falpic-ai%2Fapps-sdk-template)
Alpic supports ChatGPT apps written in any language and framework. You can of course use your favorite framework to
build your ChatGPT App.
## Hosting your ChatGPT App assets on Alpic
Alpic provides the `/assets` endpoint on all deployed servers. This allows you to serve static files like images, JavaScript, and CSS files used by your ChatGPT App.
The `/assets` endpoint is populated in the following way:
* We look for assets in the `/assets` folder at the project root location (your repository root by default)
* We look for assets built during deployment and stored in the `/assets` folder in the build **output directory** specified in your Settings. By default, this is the `dist` folder, so we look for assets in the `dist/assets` folder by default
* If both folders contain assets, a merge is done, with priority given to freshly built assets in case of conflict
You can access your assets in your MCP server tools and resources code by using the relative path `/assets`.
Note that OpenAI heavily caches ChatGPT Apps assets. In development mode, you can go to **Settings** > **Apps & Connectors**, then select your app and click on "**Refresh**" to clear the cache.
# Builds
Source: https://docs.alpic.ai/build-deploy/builds
Learn how Alpic builds and configures your MCP server during deployment
When you deploy your MCP server to Alpic, the platform automatically analyzes your project to detect configuration and build requirements. This ensures your server is configured correctly without any manual setup.
## Build Configuration
Alpic automatically detects your MCP framework, build commands, and transport type from your repository. You can customize build settings if needed.
Alpic detects configuration from your project source code. Most projects work out of the box without additional
configuration.
### Configuration hierarchy
1. **alpic.json**: Branch-specific configuration located in your project's repository root directory.
2. **Project settings**: Set in the Alpic UI and applied to all environments.
3. **Default configuration**: Automatically detected by Alpic.
### Configuring projects with alpic.json
This file should be created in your project's root directory and allows you to set:
* [`$schema`](#schema-autocomplete)
* [`buildCommand`](#buildcommand)
* [`buildOutputDir`](#buildoutputdir)
* [`installCommand`](#installcommand)
* [`startCommand`](#startcommand)
#### Schema Autocomplete
To enable autocompletion, type checking, and schema validation to your alpic.json file, add the following to the top of your file:
```json theme={null}
{
"$schema": "https://assets.alpic.ai/alpic.json"
}
```
#### buildCommand
**Type**: `string`
Use `buildCommand` to override the **Build Command** in the Project Settings dashboard.
```json theme={null}
{
"$schema": "https://assets.alpic.ai/alpic.json",
"buildCommand": "npm run build"
}
```
#### buildOutputDir
**Type**: `string`
Use `buildOutputDir` to override the **Output Directory** in the Project Settings dashboard.
```json theme={null}
{
"$schema": "https://assets.alpic.ai/alpic.json",
"buildOutputDir": "server/dist"
}
```
#### installCommand
**Type**: `string`
Use `installCommand` to override the **Install Command** in the Project Settings dashboard.
```json theme={null}
{
"$schema": "https://assets.alpic.ai/alpic.json",
"installCommand": "npm ci"
}
```
#### startCommand
**Type**: `string`
Use `startCommand` to override the **Start Command** in the Project Settings dashboard.
```json theme={null}
{
"$schema": "https://assets.alpic.ai/alpic.json",
"startCommand": "npm run --silent start"
}
```
## Transport Sourcing During Deployment
Alpic automatically detects which transport type your MCP server uses by analyzing your project source code. Detection follows a priority order, using the first match found:
### xmcp.config.ts configuration file (highest priority)
If `xmcp.config.ts` exists:
* `http:` → streamablehttp
* `stdio:` → stdio
### TypeScript imports
Searches for imports from `@modelcontextprotocol/sdk/server/`:
* `stdio` → stdio
* `sse` → sse
* `streamableHttp` → streamablehttp
### Python mcp.run() or mcp.http\_app() calls
Searches for explicit transport parameters:
* `transport="stdio"` → stdio
* `transport="sse"` → sse
* `transport="streamable-http"` → streamable-http
* `transport="http"` → http
### Python mcp.run() fallback (lowest priority)
If `mcp.run()` is found without an explicit transport → defaults to stdio
## Python Project Setup
Alpic uses [uv](https://docs.astral.sh/uv/) as the default Python package manager. By default, Alpic expects your Python project to use a **uv project** structure with a `pyproject.toml` file. If your project follows this convention, no additional configuration is needed — Alpic will automatically detect and install your dependencies.
### Using `requirements.txt` instead of `pyproject.toml`
If your project uses a simple `main.py` file with a `requirements.txt` for dependencies, you need to override the install command either in your **Project Settings** or in your `alpic.json`:
```json theme={null}
{
"$schema": "https://assets.alpic.ai/alpic.json",
"installCommand": "uv venv && uv pip install -r requirements.txt"
}
```
The default start command (`uv run main.py`) works as-is, so you don't need to override `startCommand`.
We recommend migrating to a `pyproject.toml`-based uv project for the best experience. You can initialize one by
running `uv init` in your project directory.
## Troubleshooting
### Build Fails: "No MCP transport found"
If Alpic cannot detect a transport type, the build will fail. To resolve:
1. Create an `xmcp.config.ts` file with your transport type
2. Ensure you're importing from the correct MCP SDK path (TypeScript)
3. Add an explicit transport parameter to `mcp.run()` (Python)
Make sure your project uses one of the supported MCP frameworks. Alpic automatically detects transport for projects
using the official MCP SDKs.
# Alpic Endpoints
Source: https://docs.alpic.ai/build-deploy/endpoints
Learn about the endpoints and assets exposed by Alpic
## Alpic architecture overview
To allow zero-configuration deployments of MCP servers and Apps that scale with your users, Alpic uses a serverless architecture. Alpic exposes an MPC gateway that handles protocol-specific specificities like transport and authentication, and proxies requests to the MCP server built from your connected repository.
This means that your repository **needs to expose an MCP server endpoint** that can be accessed by the Alpic gateway, either locally in stdio, or via SSE or Streamable HTTP.
## Public Endpoints
All deployed servers are hosted by default on domains in the form of `https://my-domain-123456.alpic.live`. Each environment receives its own unique domain that you can use to connect your MCP clients.
Here is the list of available endpoints on your MCP server domain:
* `/` - Main MCP Server Endpoint. It supports both **SSE** and **StreamableHTTP** transports.
* `/mcp` - it supports **StreamableHTTP** transport only and is used for compatibility with certain MCP Clients.
* `/try` - The [Playground](/distribution/playground), an AI-powered interface where users can try your MCP server directly from their browser.
## Internal Endpoints
The following endpoints are used internally by Alpic for authentication and server management.
#### OAuth Authentication Endpoints
These endpoints are only available if your server is not public and requires authentication:
* **`/.well-known/protected-resource-metadata`**: Part of the OAuth authentication flow, provides metadata about protected resources
* **`/.well-known/oauth-authorization-server`**: Part of the OAuth authentication flow. Only available if authentication is not delegated to an external authorization server
#### Dynamic Client Registration (DCR) Proxy Endpoints
These endpoints are only available if Dynamic Client Registration (DCR) proxy is activated:
* **`/register`**: Endpoint for client registration
* **`/authorize`**: OAuth authorization endpoint
* **`/token`**: OAuth token endpoint
* **`/callback`**: OAuth callback endpoint
## Static Assets
**`/assets`**: Static assets exposed by your MCP server. This endpoint serves any static files you've configured for deployment in your repository.
Learn more about how static assets are generated and served by Alpic in our guide on [Hosting ChatGPT Apps](/build-deploy/assets#hosting-your-chatgpt-app-assets-on-alpic).
# Environment Variables
Source: https://docs.alpic.ai/build-deploy/env-variables
System-provided environment variables available in your MCP server deployments
Alpic automatically provides system environment variables to your MCP server deployments. These variables are set automatically and cannot be overridden by users.
## System environment variables
### ALPIC\_HOST
**Available at:** Both build and runtime
The hostname of your environment's MCP server URL. Used internally to treat the deployed host as an internal host (like `localhost`) when it's used as the OAuth server.
```
ALPIC_HOST=server-123456.alpic.live
```
### ALPIC\_CUSTOM\_DOMAINS
**Available at:** Both build and runtime
A comma-separated list of custom domains that are configured for your environment. Used internally to treat the deployed host as an internal host (like `localhost`) when it's used as the OAuth server.
```
ALPIC_CUSTOM_DOMAINS=yourdomain.com,anotherdomain.com
```
# Environments
Source: https://docs.alpic.ai/build-deploy/environments
How to manage your deployment environments
Alpic allows you to create multiple deployment environments for your MCP server. Each environment is linked to a specific branch of your git repository and corresponds to a different version of your MCP server.
To create a new environment:
1. Go to your project overview page.
2. Click on **Environments**.
3. Click on **New environment**.
4. Name your new environment.
5. Select the branch you want to link to this environment.
6. Add any environment variables you need for this environment.
7. Click **Create**.
This will generate a new URL that you can use to access this environment's MCP server.
## Changing the tracked branch
You can change the branch linked to an environment after creation:
1. Go to your project overview page.
2. Click on **Environments** and select the environment you want to update.
3. Click the edit icon next to the current branch name.
4. Select or type the new branch name and click **Save**.
You'll be notified of the branch tracking update result and you'll get a chance to trigger a redeploy with the latest code from the new branch if the update was successful.
You can delete environments at any time by clicking the **Delete** button in your environment settings.
# audit
Source: https://docs.alpic.ai/cli/audit
Run a Beacon audit on an MCP server
Runs a [Beacon](/testing/beacon) audit against a remote MCP server and prints a summary of passed, failed, and skipped checks. Beacon verifies spec compliance, tool and resource metadata, and readiness for ChatGPT and Claude.ai.
End-to-end widget rendering checks are skipped from the CLI because they take several minutes per platform. Run the
full audit — including live browser widget tests — from the Beacon tab in the Alpic dashboard.
## Usage
Audit a server by URL (standalone mode, no project association):
```bash theme={null}
alpic audit --url https://my-server.example.com/mcp
```
Runs the audit against the specified MCP server URL and prints a summary report with passed, failed, and skipped checks.
## Extended Usage
Output the full report as JSON (useful for CI or further processing):
```bash theme={null}
alpic audit --url https://my-server.example.com/mcp --json
```
Skip confirmation prompts:
```bash theme={null}
alpic audit --url https://my-server.example.com/mcp --non-interactive
```
## Options
| Flag | Description | Default |
| ------------------- | ---------------------------------------------------------------------------------- | ------- |
| `--url` | The HTTPS URL of the MCP server to audit (standalone mode, no project association) | |
| `--json` | Output the full report as JSON | `false` |
| `--team-id` | Team ID (only with `--url` for standalone audits) | |
| `--project-id` | Project ID to audit | |
| `--environment-id` | Environment ID to audit (defaults to production) | |
| `--non-interactive` | Skip all confirmation prompts | `false` |
## Exit codes
`alpic audit` exits with code `1` when at least one error-severity check fails, and `0` otherwise. Warnings and skipped checks do not fail the command, so you can gate CI on blocking issues while still surfacing lower-severity findings in the output.
Authentication is required. Run `alpic login` or set `ALPIC_API_KEY` before running an audit.
If the current directory is linked to an Alpic project, the audit automatically associates with that project. Use
`--non-interactive` to skip the confirmation prompt.
Beacon only supports unauthenticated MCP servers today. If the target returns `HTTP 401`, Beacon reports that auth is
required and skips the remaining checks.
# deploy
Source: https://docs.alpic.ai/cli/deploy
Deploy a project to Alpic
Package and deploy an MCP server project to Alpic. The command collects source files, uploads them, and waits for the deployment to complete before printing the server URL.
## Usage
```bash theme={null}
alpic deploy
```
Deploys the current directory. Prompts for any missing configuration (project name, runtime) if not already set.
## Extended Usage
Skip all prompts (useful in CI):
```bash theme={null}
alpic deploy --non-interactive --project-name my-app --runtime node24
```
Create and deploy a new project whose app root is a monorepo subdirectory (path is relative to the directory where you run the command):
```bash theme={null}
alpic deploy --project-name my-app --root-dir ./packages/my-mcp-server
```
`--root-dir` is passed through the same linking flow as `alpic link`: it only applies when this run **creates** a new Alpic project. For an already linked directory, the app root was set at link/create time.
Import environment variables from a `.env` file when creating a new project:
```bash theme={null}
alpic deploy --project-name my-app --runtime node24 --env-file .env
```
## Options
| Flag | Description |
| ------------------- | ------------------------------------------------------------------------------------------------------------------------- |
| `--non-interactive` | Skip all confirmation prompts |
| `--runtime` | Runtime to use: `node24`, `node22`, `python3.14`, `python3.13` |
| `--root-dir` | Relative path to the app root for builds and deployments; only when **creating** a new project (omit for repository root) |
| `--project-name` | Name for a new project |
| `--team-id` | Team ID |
| `--project-id` | Project ID to deploy to directly |
| `--environment-id` | Environment ID to deploy to directly |
| `--env-file` | Path to a `.env` file to source environment variables from (new projects only) |
Project names can be renamed. For CI/CD pipelines, prefer `--project-id` to avoid breakage.Authentication is required. Run `alpic login` or set `ALPIC_API_KEY` before deploying.
# deployment inspect
Source: https://docs.alpic.ai/cli/deployment-inspect
Show details of a deployment
Displays detailed information about a specific deployment, including its status, environment, author, source branch, commit, and duration.
## Usage
```bash theme={null}
alpic deployment inspect --deployment-id
```
Fetches and prints the details of the specified deployment.
## Extended Usage
Inspect the latest deployment for an environment:
```bash theme={null}
alpic deployment inspect --environment-id
```
Wait for an in-progress deployment to reach a final state before printing its details:
```bash theme={null}
alpic deployment inspect --deployment-id --wait
```
## Options
| Flag | Description | Default |
| ------------------ | ---------------------------------------------------------------------------------------------- | ------- |
| `--deployment-id` | The ID of the deployment. Mutually exclusive with `--environment-id`. | — |
| `--environment-id` | Inspect the latest deployment for this environment. Mutually exclusive with `--deployment-id`. | — |
| `--wait` | Wait for the deployment to reach a final state (deployed, failed, or canceled). | `false` |
# deployment list
Source: https://docs.alpic.ai/cli/deployment-list
List deployments for the current project
Lists all deployments for the current project, showing their ID, age, environment, status, whether the deployment is current, duration, and author.
## Usage
```bash theme={null}
alpic deployment list
```
Lists all deployments for the project linked to the current directory.
## Extended Usage
List deployments by project ID:
```bash theme={null}
alpic deployment list --project-id
```
List deployments by project name:
```bash theme={null}
alpic deployment list --project-name
```
Filter deployments by environment:
```bash theme={null}
alpic deployment list --environment-id
```
Filter by one or more statuses:
```bash theme={null}
alpic deployment list --status deployed --status failed
```
## Options
| Flag | Description | Default |
| ------------------ | ---------------------------------------------------------------------------------------------------------------- | ------- |
| `--project-id` | The ID of the project. | — |
| `--project-name` | The name of the project. | — |
| `--environment-id` | Filter by environment ID. | — |
| `--status` | Filter by status. Accepted values: `deployed`, `ongoing`, `failed`, `canceled`. Can be specified multiple times. | — |
# deployment logs
Source: https://docs.alpic.ai/cli/deployment-logs
Retrieve build logs for a deployment
Retrieves the build logs for a specific deployment or the latest deployment of an environment.
## Usage
```bash theme={null}
alpic deployment logs --deployment-id
```
Prints the build logs for the specified deployment.
## Extended Usage
Follow logs while the build is still running:
```bash theme={null}
alpic deployment logs --deployment-id --follow
```
Retrieve logs for the latest deployment of an environment:
```bash theme={null}
alpic deployment logs --environment-id
```
Disable colorized output:
```bash theme={null}
alpic deployment logs --deployment-id --no-color
```
## Options
| Flag | Description | Default |
| ------------------ | --------------------------------------------------------------------------------------------------- | ------- |
| `--deployment-id` | The ID of the deployment (e.g. `dpl_xxx`). Mutually exclusive with `--environment-id`. | — |
| `--environment-id` | Show logs for the latest deployment of this environment. Mutually exclusive with `--deployment-id`. | — |
| `-f, --follow` | Poll for new logs while the build is still running. | `false` |
| `--no-color` | Disable colorized output. | `false` |
# environment inspect
Source: https://docs.alpic.ai/cli/environment-inspect
Show details of an environment
Displays detailed information about an environment, including its ID, name, project, team, source branch, MCP server URL, custom domains, and creation date.
## Usage
```bash theme={null}
alpic environment inspect --environment-id
```
Shows details of the specified environment.
## Extended Usage
Inspect an environment by name:
```bash theme={null}
alpic environment inspect --environment-name --project-name
```
When no identifier is provided, you will be prompted to select a project and then an environment interactively:
```bash theme={null}
alpic environment inspect
```
## Options
| Flag | Description | Default |
| -------------------- | --------------------------------------------------------------------------------- | ------- |
| `--environment-id` | The ID of the environment. | — |
| `--environment-name` | The name of the environment. | — |
| `--project-id` | The ID of the project. | — |
| `--project-name` | The name of the project. | — |
| `--non-interactive` | Disable interactive prompts. Requires `--environment-id` or `--environment-name`. | `false` |
# environment list
Source: https://docs.alpic.ai/cli/environment-list
List environments for a project
Lists all environments for a project, showing their ID, name, project, team, source branch, MCP server URL, and creation date.
## Usage
```bash theme={null}
alpic environment list --project-id
```
Lists all environments for the specified project.
## Extended Usage
List environments by project name:
```bash theme={null}
alpic environment list --project-name
```
When no project identifier is provided, you will be prompted to select a project interactively:
```bash theme={null}
alpic environment list
```
## Options
| Flag | Description | Default |
| ------------------- | ------------------------------------------------------------------------- | ------- |
| `--project-id` | The ID of the project. | — |
| `--project-name` | The name of the project. | — |
| `--non-interactive` | Disable interactive prompts. Requires `--project-id` or `--project-name`. | `false` |
# environment-variable add
Source: https://docs.alpic.ai/cli/environment-variable-add
Add one or more environment variables to an Alpic environment
Adds environment variables to an Alpic environment. Runs interactively by default. Variables are marked as secret by default, meaning their values are masked when listed.
## Usage
```bash theme={null}
alpic environment-variable add
```
Prompts for a key, value, and secret preference interactively.
## Extended Usage
**Add a single variable non-interactively:**
```bash theme={null}
alpic environment-variable add --key DATABASE_URL --value postgres://localhost/db
```
**Add a non-secret (visible) variable:**
```bash theme={null}
alpic environment-variable add --key PORT --value 3000 --no-secret
```
**Import all variables from a `.env` file:**
```bash theme={null}
alpic environment-variable add --env-file .env
```
**Import from a `.env` file into a specific environment:**
```bash theme={null}
alpic environment-variable add --env-file .env --environment-id
```
## Options
| Flag | Description |
| ------------------- | ----------------------------------------------------------------------------- |
| `--environment-id` | The ID of the environment (optional — read from local config if omitted) |
| `--key` | The environment variable key |
| `--value` | The environment variable value |
| `--secret` | Mark as secret (default: true). Use `--no-secret` to store in plain text |
| `--env-file` | Path to a `.env` file to import (mutually exclusive with `--key`/`--value`) |
| `--non-interactive` | Disable interactive prompts. Requires `--key` and `--value` (or `--env-file`) |
When running from a directory linked to an Alpic project (via `alpic deploy`), `--environment-id` is optional — the
CLI reads it from the local `.alpic` config file.
# environment-variable list
Source: https://docs.alpic.ai/cli/environment-variable-list
List all environment variables for an Alpic environment
Lists all environment variables for an Alpic environment. Secret variable values are displayed as ``.
## Usage
```bash theme={null}
alpic environment-variable list
```
Lists all environment variables for the current linked environment.
## Extended Usage
**List variables for a specific environment by ID:**
```bash theme={null}
alpic environment-variable list --environment-id
```
## Options
| Flag | Description |
| ------------------ | ------------------------------------------------------------------------ |
| `--environment-id` | The ID of the environment (optional — read from local config if omitted) |
When running from a directory linked to an Alpic project (via `alpic deploy`), `--environment-id` is optional — the
CLI reads it from the local `.alpic` config file.
# environment-variable remove
Source: https://docs.alpic.ai/cli/environment-variable-remove
Remove an environment variable from an Alpic environment
Removes an environment variable from an Alpic environment. Prompts to select the key interactively unless `--key` is provided.
## Usage
```bash theme={null}
alpic environment-variable remove
```
Prompts to select the variable to remove interactively.
## Extended Usage
**Remove a specific variable non-interactively:**
```bash theme={null}
alpic environment-variable remove --key DATABASE_URL
```
**Remove a variable from a specific environment:**
```bash theme={null}
alpic environment-variable remove --key DATABASE_URL --environment-id
```
## Options
| Flag | Description |
| ------------------- | ------------------------------------------------------------------------ |
| `--environment-id` | The ID of the environment (optional — read from local config if omitted) |
| `--key` | The key of the environment variable to remove |
| `--non-interactive` | Disable interactive prompts. Requires `--key` |
When running from a directory linked to an Alpic project (via `alpic deploy`), `--environment-id` is optional — the
CLI reads it from the local `.alpic` config file.
# environment-variable update
Source: https://docs.alpic.ai/cli/environment-variable-update
Update the value and/or secret status of an environment variable
Updates the value and/or secret status of an existing environment variable in an Alpic environment.
## Usage
```bash theme={null}
alpic environment-variable update
```
Prompts to select the variable and enter the new value interactively.
## Extended Usage
**Update a variable's value:**
```bash theme={null}
alpic environment-variable update --key DATABASE_URL --value postgres://prod/db
```
**Update a variable in a specific environment:**
```bash theme={null}
alpic environment-variable update --key PORT --value 8080 --environment-id
```
**Change a variable's secret status:**
```bash theme={null}
alpic environment-variable update --key PORT --no-secret
```
## Options
| Flag | Description |
| ------------------- | ---------------------------------------------------------------------------------------------- |
| `--environment-id` | The ID of the environment (optional — read from local config if omitted) |
| `--key` | The key of the environment variable to update |
| `--value` | The new value (omit to keep the current value) |
| `--secret` | Mark as secret. Use `--no-secret` to make it visible |
| `--non-interactive` | Disable interactive prompts. Requires `--key` and one of `--value` or `--secret`/`--no-secret` |
When running from a directory linked to an Alpic project (via `alpic deploy`), `--environment-id` is optional — the
CLI reads it from the local `.alpic` config file.
# git connect
Source: https://docs.alpic.ai/cli/git-connect
Connect a linked Alpic project to a git remote source
Links an Alpic project to a GitHub remote repository. Once connected, you can install the Alpic GitHub App to trigger automatic deployments on every push.
The directory must already be linked to an Alpic project (via `alpic deploy`) and must be a git repository with at
least one GitHub remote configured.
## Usage
```bash theme={null}
alpic git connect
```
Detects GitHub remotes in the current directory and prompts to select one.
## Extended Usage
```bash theme={null}
# Auto-select the only available remote without prompting
alpic git connect --non-interactive
# Connect using a specific named remote
alpic git connect --remote-name origin
```
## Options
| Flag | Description | Default |
| ------------------- | ---------------------------------------------------------- | ------- |
| `--non-interactive` | Automatically select the remote when only one is available | `false` |
| `--remote-name` | Name of the remote to connect | |
# git disconnect
Source: https://docs.alpic.ai/cli/git-disconnect
Disconnect a linked Alpic project from its git remote source
Removes the git remote repository link from an Alpic project. The project remains deployed; only the source repository association is cleared.
## Usage
```bash theme={null}
alpic git disconnect
```
Prompts for confirmation before removing the git remote link from the linked project in the current directory.
## Extended Usage
```bash theme={null}
# Skip the confirmation prompt
alpic git disconnect --non-interactive
```
## Options
| Flag | Description | Default |
| ------------------- | ---------------------------- | ------- |
| `--non-interactive` | Skip the confirmation prompt | `false` |
# insights category create
Source: https://docs.alpic.ai/cli/insights-category-create
Create an intent category in an Alpic environment
Create an intent category, optionally linking it to existing intents.
## Usage
```bash theme={null}
alpic insights category create
```
Prompts for a name and creates the category in the environment from the local `.alpic` configuration.
## Extended Usage
Create a category with an explicit name and color:
```bash theme={null}
alpic insights category create --name Scheduling --color blue
```
When `--color` is omitted, a color is derived from the category name. Available colors: `red`, `orange`, `yellow`, `green`, `blue`, `purple`, `pink`, `gray`.
Link the new category to existing intents right away:
```bash theme={null}
alpic insights category create \
--name Scheduling \
--intent-id \
--intent-id
```
Create in automation and print the created category:
```bash theme={null}
alpic insights category create --environment-id --name Scheduling --json --non-interactive
```
`--name` is required in non-interactive mode.
## Options
| Flag | Description | Default |
| -------------------- | ------------------------------------------------------ | ------- |
| `--environment-id` | Select an environment by ID | |
| `--environment-name` | Select an environment by name | |
| `--project-id` | Provide the project context by ID | |
| `--project-name` | Provide the project context by name | |
| `--name` | The category name | |
| `--color` | The category color; derived from the name when omitted | |
| `--intent-id` | Link the new category to this intent ID. Repeatable. | |
| `--json` | Print the unmodified API response as JSON | `false` |
| `--non-interactive` | Skip interactive prompts | |
When the current directory is linked to an environment, the environment and project flags can be omitted.
# insights category delete
Source: https://docs.alpic.ai/cli/insights-category-delete
Delete an intent category
Delete an intent category. Intents linked to the category are unlinked but not deleted.
## Usage
```bash theme={null}
alpic insights category delete
```
Prompts for the category to delete and asks for confirmation.
## Extended Usage
Select the category explicitly:
```bash theme={null}
alpic insights category delete --category-id
```
Delete in automation:
```bash theme={null}
alpic insights category delete \
--environment-id \
--category-id \
--non-interactive
```
`--category-id` is required in non-interactive mode, and the confirmation prompt is skipped.
## Options
| Flag | Description | Default |
| -------------------- | ----------------------------------- | ------- |
| `--environment-id` | Select an environment by ID | |
| `--environment-name` | Select an environment by name | |
| `--project-id` | Provide the project context by ID | |
| `--project-name` | Provide the project context by name | |
| `--category-id` | The ID of the intent category | |
| `--non-interactive` | Skip interactive prompts | |
When the current directory is linked to an environment, the environment and project flags can be omitted.
# insights category link
Source: https://docs.alpic.ai/cli/insights-category-link
Link an intent category to intents
Link an intent category to one or more intents. Intents that already carry the category are ignored.
Intent IDs are returned by [`insights intent list`](/cli/insights-intent-list) in the `--json` output (`intentIds`).
## Usage
```bash theme={null}
alpic insights category link --intent-id
```
Prompts for the category to link in the environment from the local `.alpic` configuration.
## Extended Usage
Link several intents to an explicit category:
```bash theme={null}
alpic insights category link \
--category-id \
--intent-id \
--intent-id
```
Link in automation:
```bash theme={null}
alpic insights category link \
--environment-id \
--category-id \
--intent-id \
--non-interactive
```
`--category-id` is required in non-interactive mode.
## Options
| Flag | Description | Default |
| -------------------- | ---------------------------------------- | ------- |
| `--environment-id` | Select an environment by ID | |
| `--environment-name` | Select an environment by name | |
| `--project-id` | Provide the project context by ID | |
| `--project-name` | Provide the project context by name | |
| `--category-id` | The ID of the intent category | |
| `--intent-id` | The ID of an intent to link. Repeatable. | |
| `--non-interactive` | Skip interactive prompts | |
When the current directory is linked to an environment, the environment and project flags can be omitted.
# insights category list
Source: https://docs.alpic.ai/cli/insights-category-list
List intent categories for an Alpic environment
List the intent categories of an environment, including how many intents each category is linked to.
## Usage
```bash theme={null}
alpic insights category list
```
Lists categories for the environment in the local `.alpic` configuration.
## Extended Usage
Select an environment by ID:
```bash theme={null}
alpic insights category list --environment-id
```
Select an environment by name within a project:
```bash theme={null}
alpic insights category list --project-name my-project --environment-name production
```
Emit the complete API response for automation:
```bash theme={null}
alpic insights category list --environment-id --json --non-interactive
```
## Options
| Flag | Description | Default |
| -------------------- | ----------------------------------------- | ------- |
| `--environment-id` | Select an environment by ID | |
| `--environment-name` | Select an environment by name | |
| `--project-id` | Provide the project context by ID | |
| `--project-name` | Provide the project context by name | |
| `--json` | Print the unmodified API response as JSON | `false` |
| `--non-interactive` | Skip interactive prompts | |
When the current directory is linked to an environment, the environment and project flags can be omitted.
# insights category unlink
Source: https://docs.alpic.ai/cli/insights-category-unlink
Unlink an intent category from intents
Remove an intent category from one or more intents. Intents that do not carry the category are ignored.
## Usage
```bash theme={null}
alpic insights category unlink --intent-id
```
Prompts for the category to unlink in the environment from the local `.alpic` configuration.
## Extended Usage
Unlink several intents from an explicit category:
```bash theme={null}
alpic insights category unlink \
--category-id \
--intent-id \
--intent-id
```
Unlink in automation:
```bash theme={null}
alpic insights category unlink \
--environment-id \
--category-id \
--intent-id \
--non-interactive
```
`--category-id` is required in non-interactive mode.
## Options
| Flag | Description | Default |
| -------------------- | ------------------------------------------ | ------- |
| `--environment-id` | Select an environment by ID | |
| `--environment-name` | Select an environment by name | |
| `--project-id` | Provide the project context by ID | |
| `--project-name` | Provide the project context by name | |
| `--category-id` | The ID of the intent category | |
| `--intent-id` | The ID of an intent to unlink. Repeatable. | |
| `--non-interactive` | Skip interactive prompts | |
When the current directory is linked to an environment, the environment and project flags can be omitted.
# insights category update
Source: https://docs.alpic.ai/cli/insights-category-update
Rename or recolor an intent category
Rename or recolor an intent category.
## Usage
```bash theme={null}
alpic insights category update --name "New name"
```
Prompts for the category to update in the environment from the local `.alpic` configuration.
## Extended Usage
Select the category explicitly and change both fields:
```bash theme={null}
alpic insights category update \
--category-id \
--name "New name" \
--color green
```
Update in automation:
```bash theme={null}
alpic insights category update \
--environment-id \
--category-id \
--color green \
--non-interactive
```
At least one of `--name` or `--color` is required. `--category-id` is required in non-interactive mode.
## Options
| Flag | Description | Default |
| -------------------- | ----------------------------------------- | ------- |
| `--environment-id` | Select an environment by ID | |
| `--environment-name` | Select an environment by name | |
| `--project-id` | Provide the project context by ID | |
| `--project-name` | Provide the project context by name | |
| `--category-id` | The ID of the intent category | |
| `--name` | The new category name | |
| `--color` | The new category color | |
| `--json` | Print the unmodified API response as JSON | `false` |
| `--non-interactive` | Skip interactive prompts | |
When the current directory is linked to an environment, the environment and project flags can be omitted.
# insights feedback list
Source: https://docs.alpic.ai/cli/insights-feedback-list
List user feedback for an Alpic environment
List feedback captured from users and models, including its source, client, and timestamp.
## Usage
```bash theme={null}
alpic insights feedback list
```
Lists feedback from the last seven days for the environment in the local `.alpic` configuration.
## Extended Usage
Select an environment by ID and use an explicit time range:
```bash theme={null}
alpic insights feedback list \
--environment-id \
--since 24h \
--until 1h
```
Select an environment by name within a project:
```bash theme={null}
alpic insights feedback list --project-name my-project --environment-name production
```
The project can also be selected by ID:
```bash theme={null}
alpic insights feedback list --project-id --environment-name production
```
Filter and page through feedback:
```bash theme={null}
alpic insights feedback list \
--search timeout \
--client-id claude-desktop \
--limit 100 \
--offset 100
```
Emit the complete API response for automation:
```bash theme={null}
alpic insights feedback list --environment-id --json --non-interactive
```
Human-readable output truncates long feedback content. `--json` preserves complete content.
## Options
| Flag | Description | Default |
| -------------------- | --------------------------------------------------------- | ------- |
| `--environment-id` | Select an environment by ID | |
| `--environment-name` | Select an environment by name | |
| `--project-id` | Provide the project context by ID | |
| `--project-name` | Provide the project context by name | |
| `--since` | Show feedback after a relative duration or ISO 8601 date | `7d` |
| `--until` | Show feedback before a relative duration or ISO 8601 date | now |
| `--search` | Search feedback content and source | |
| `--client-id` | Filter by stable MCP client ID. Repeatable. | |
| `--limit` | Return between 1 and 200 feedback entries | `50` |
| `--offset` | Skip this many feedback entries | `0` |
| `--json` | Print the unmodified API response as JSON | `false` |
| `--non-interactive` | Skip interactive prompts | |
When the current directory is linked to an environment, the environment and project flags can be omitted.
# insights intent list
Source: https://docs.alpic.ai/cli/insights-intent-list
List grouped user intents for an Alpic environment
List grouped examples of what users tried to accomplish, including occurrence counts, tools, clients, and categories.
## Usage
```bash theme={null}
alpic insights intent list
```
Lists intents from the last seven days for the environment in the local `.alpic` configuration.
## Extended Usage
Select an environment by ID and use an explicit time range:
```bash theme={null}
alpic insights intent list \
--environment-id \
--since 2026-08-01T00:00:00Z \
--until 2026-08-08T00:00:00Z
```
Select an environment by name within a project:
```bash theme={null}
alpic insights intent list --project-name my-project --environment-name production
```
The project can also be selected by ID:
```bash theme={null}
alpic insights intent list --project-id --environment-name production
```
Filter and page through grouped intents:
```bash theme={null}
alpic insights intent list \
--search scheduling \
--tool create-event \
--tool update-event \
--client-id claude-desktop \
--category-id \
--sort asc \
--limit 100 \
--offset 100
```
Emit the complete API response for automation:
```bash theme={null}
alpic insights intent list --environment-id --json --non-interactive
```
Human-readable output truncates long intent messages. `--json` preserves complete messages.
## Options
| Flag | Description | Default |
| -------------------- | -------------------------------------------------------- | ------- |
| `--environment-id` | Select an environment by ID | |
| `--environment-name` | Select an environment by name | |
| `--project-id` | Provide the project context by ID | |
| `--project-name` | Provide the project context by name | |
| `--since` | Show intents after a relative duration or ISO 8601 date | `7d` |
| `--until` | Show intents before a relative duration or ISO 8601 date | now |
| `--search` | Search intent messages, tools, and categories | |
| `--tool` | Filter by tool name. Repeatable. | |
| `--client-id` | Filter by stable MCP client ID. Repeatable. | |
| `--category-id` | Filter by category ID. Repeatable. | |
| `--sort` | Sort by date: `asc` or `desc` | `desc` |
| `--limit` | Return between 1 and 200 grouped intents | `50` |
| `--offset` | Skip this many grouped intents | `0` |
| `--json` | Print the unmodified API response as JSON | `false` |
| `--non-interactive` | Skip interactive prompts | |
When the current directory is linked to an environment, the environment and project flags can be omitted.
# link
Source: https://docs.alpic.ai/cli/link
Link the current directory to an Alpic project
Associate the current directory with an existing or new Alpic project. Once linked, commands like `deploy` and `git connect` operate on the linked project without requiring additional flags.
## Usage
```bash theme={null}
alpic link
```
Interactively links the current directory to an Alpic project. Prompts for project selection or creation if not already linked.
## Extended Usage
Skip all prompts (useful in CI):
```bash theme={null}
alpic link --non-interactive --project-name my-app --runtime node24
```
Link to an existing project by ID:
```bash theme={null}
alpic link --project-id
```
Create a new project whose app lives in a monorepo subdirectory (path is relative to the directory where you run the command):
```bash theme={null}
alpic link --project-name my-app --root-dir ./packages/my-mcp-server
```
When linking interactively, **Root directory** defaults to `.` (repository root). Alpic stores this path on the project so builds and deployments run from that folder inside the connected repository.
## Options
| Flag | Description |
| ------------------- | ------------------------------------------------------------------------------------------------------------------------- |
| `--non-interactive` | Skip all confirmation prompts |
| `--team-id` | Team ID to use |
| `--project-id` | Link to this existing project ID |
| `--environment-id` | Use this environment ID |
| `--project-name` | New project name |
| `--runtime` | Runtime to use: `node24`, `node22`, `python3.14`, `python3.13` |
| `--root-dir` | Relative path to the app root for builds and deployments; only when **creating** a new project (omit for repository root) |
| `--env-file` | Path to a `.env` file to source environment variables from (new projects only) |
Authentication is required. Run `alpic login` or set `ALPIC_API_KEY` before linking.
If the current directory is a git repository, `link` will offer to connect the project to a GitHub remote after
linking.
# login
Source: https://docs.alpic.ai/cli/login
Log in to Alpic using browser-based OAuth authentication
Authenticate with Alpic by completing an OAuth flow in your browser. Tokens are stored locally and reused across sessions.
## Usage
```bash theme={null}
alpic login
```
Opens a browser window to complete authentication. Once done, credentials are saved locally so subsequent commands run without re-authenticating.
If already logged in, the command exits immediately without opening the browser.
## Options
This command has no flags.
`login` only applies to browser-based authentication. If you use an API key via `ALPIC_API_KEY`, no login is required.
# logout
Source: https://docs.alpic.ai/cli/logout
Log out from Alpic and remove stored OAuth credentials
Removes the OAuth credentials stored on this machine. Has no effect if no credentials are stored.
## Usage
```bash theme={null}
alpic logout
```
Clears locally stored OAuth tokens.
## Extended Usage
```bash theme={null}
# Log out and then log back in as a different user
alpic logout
alpic login
```
This command only removes OAuth tokens. If you authenticate via `ALPIC_API_KEY`, unset the environment variable to
stop using that key.
## Options
This command has no flags.
# logs
Source: https://docs.alpic.ai/cli/logs
Stream runtime logs for an Alpic environment
Stream runtime logs for an environment. Fetches recent log entries and optionally follows new ones as they arrive.
## Usage
```bash theme={null}
alpic logs --environment-id
```
Fetches log entries for the specified environment.
## Extended Usage
Show logs from the last hour:
```bash theme={null}
alpic logs --environment-id --since 1h
```
Follow logs continuously as new entries arrive:
```bash theme={null}
alpic logs --environment-id --follow
```
When `--follow` is used without `--since`, the CLI defaults to showing logs from the last 10 minutes before polling
for new entries.
Show logs up to a specific point in time:
```bash theme={null}
alpic logs --environment-id --since 1h --until 30m
```
`--until` cannot be used together with `--follow`.
Filter by one or more log levels:
```bash theme={null}
alpic logs --environment-id --level ERROR --level WARNING
```
Search log entries by keyword or filter expression:
```bash theme={null}
alpic logs --environment-id --search 'timeout' --limit 50
```
Disable colorized output:
```bash theme={null}
alpic logs --environment-id --no-color
```
## Options
| Flag | Short | Description | Default |
| ------------------ | ----- | ----------------------------------------------------------------------------------------------------- | ------- |
| `--environment-id` | | The ID of the environment | |
| `--since` | | Show logs after this time (e.g. `1h`, `30m`, `2024-01-01T00:00:00Z`) | |
| `--until` | | Show logs before this time (e.g. `1h`, `30m`, `2024-01-01T00:00:00Z`). Cannot be used with `--follow` | |
| `--limit` | `-n` | Maximum number of log entries to fetch (1–1000). Cannot be used with `--follow` | |
| `--follow` | `-f` | Poll for new logs continuously | `false` |
| `--level` | | Filter by log level (`INFO`, `ERROR`, `WARNING`, `DEBUG`). Repeatable. | |
| `--search` | | Filter logs by text or regex pattern | |
| `--no-color` | | Disable colorized output and show log levels as text | `false` |
# Alpic CLI
Source: https://docs.alpic.ai/cli/overview
Deploy and manage your MCP servers from the command line
## Installing Alpic CLI
Install the CLI globally with npm or pnpm:
```bash theme={null}
npm install -g alpic
# or
pnpm add -g alpic
```
Run it without installing via npx:
```bash theme={null}
npx alpic
```
## Authentication
The CLI supports two authentication methods: browser-based login and API key.
### Browser login (recommended)
```bash theme={null}
alpic login
```
Opens a browser window to authenticate. Tokens are stored locally and reused across sessions.
### API key
Set the `ALPIC_API_KEY` environment variable to authenticate without a browser:
In the [Alpic dashboard](https://app.alpic.ai), open your team and go to **API Keys** in the sidebar. Click **New API key**, name it, and copy the key.
Export the key in your shell or add it to your CI/CD secrets:
```bash theme={null}
export ALPIC_API_KEY=your_api_key_here
```
If both OAuth login credentials and `ALPIC_API_KEY` are present, the CLI uses `ALPIC_API_KEY`.
Keep your API key secure and never commit it to version control. If exposed, revoke it in the dashboard and create a
new one.
## Checking the version
```bash theme={null}
alpic --version
```
Prints the installed CLI version.
## Available Commands
| Command | What it does | Full reference |
| ---------------------------------- | ------------------------------------------------------------------------- | -------------------------------------------------- |
| `team list` | List teams for the current user | [View docs](/cli/team-list) |
| `team inspect` | Show details of a team and its projects | [View docs](/cli/team-inspect) |
| `project list` | List projects | [View docs](/cli/project-list) |
| `project inspect` | Show details of a project and its environments | [View docs](/cli/project-inspect) |
| `environment list` | List environments for a project | [View docs](/cli/environment-list) |
| `environment inspect` | Show details of an environment | [View docs](/cli/environment-inspect) |
| `audit` | Run a beacon audit on an MCP server | [View docs](/cli/audit) |
| `link` | Link the current directory to an Alpic project | [View docs](/cli/link) |
| `deploy` | Deploy a project from the current directory or a specified path | [View docs](/cli/deploy) |
| `publish` | Publish an MCP server to the MCP registry | [View docs](/cli/publish) |
| `deployment list` | List deployments for the current project | [View docs](/cli/deployment-list) |
| `deployment inspect` | Show details of a specific deployment | [View docs](/cli/deployment-inspect) |
| `deployment logs` | Retrieve build logs for a deployment | [View docs](/cli/deployment-logs) |
| `login` | Authenticate via browser OAuth | [View docs](/cli/login) |
| `logout` | Remove stored OAuth credentials | [View docs](/cli/logout) |
| `whoami` | Show the current authenticated identity | [View docs](/cli/whoami) |
| `logs` | Stream runtime logs for an Alpic environment | [View docs](/cli/logs) |
| `insights intent list` | List grouped user intents for an environment | [View docs](/cli/insights-intent-list) |
| `insights feedback list` | List user feedback for an environment | [View docs](/cli/insights-feedback-list) |
| `insights category list` | List intent categories for an environment | [View docs](/cli/insights-category-list) |
| `insights category create` | Create an intent category | [View docs](/cli/insights-category-create) |
| `insights category update` | Rename or recolor an intent category | [View docs](/cli/insights-category-update) |
| `insights category delete` | Delete an intent category | [View docs](/cli/insights-category-delete) |
| `insights category link` | Link an intent category to intents | [View docs](/cli/insights-category-link) |
| `insights category unlink` | Unlink an intent category from intents | [View docs](/cli/insights-category-unlink) |
| `git connect` | Link a project to a git remote repository | [View docs](/cli/git-connect) |
| `git disconnect` | Unlink a project from its git remote repository | [View docs](/cli/git-disconnect) |
| `environment-variable add` | Add one or more environment variables to an Alpic environment | [View docs](/cli/environment-variable-add) |
| `environment-variable list` | List all environment variables for an Alpic environment | [View docs](/cli/environment-variable-list) |
| `environment-variable remove` | Remove an environment variable from an Alpic environment | [View docs](/cli/environment-variable-remove) |
| `environment-variable update` | Update the value and/or secret status of an existing environment variable | [View docs](/cli/environment-variable-update) |
| `playground enable` | Enable the playground for an environment | [View docs](/cli/playground-enable) |
| `playground disable` | Disable the playground for an environment | [View docs](/cli/playground-disable) |
| `playground status` | Show playground configuration for an environment | [View docs](/cli/playground-status) |
| `playground configure` | Set the playground server name and description | [View docs](/cli/playground-configure) |
| `playground example-prompt add` | Add an example prompt to the playground (max 5) | [View docs](/cli/playground-example-prompt-add) |
| `playground example-prompt list` | List playground example prompts for an environment | [View docs](/cli/playground-example-prompt-list) |
| `playground example-prompt remove` | Remove an example prompt from the playground | [View docs](/cli/playground-example-prompt-remove) |
| `playground headers add` | Add a header to the playground configuration | [View docs](/cli/playground-headers-add) |
| `playground headers list` | List playground headers for an environment | [View docs](/cli/playground-headers-list) |
| `playground headers remove` | Remove a playground header | [View docs](/cli/playground-headers-remove) |
| `telemetry enable` | Opt this machine into anonymous CLI usage reporting | [View docs](/cli/telemetry-enable) |
| `telemetry disable` | Opt this machine out of anonymous CLI usage reporting | [View docs](/cli/telemetry-disable) |
| `telemetry status` | Show the current telemetry setting and anonymous machine ID | [View docs](/cli/telemetry-status) |
| `tunnel` | Expose a local server to the internet through an Alpic tunnel | [View docs](/cli/tunnel) |
# playground configure
Source: https://docs.alpic.ai/cli/playground-configure
Set the playground server name and description
Sets the display name and description shown in the Alpic playground for an environment.
## Usage
```bash theme={null}
alpic playground configure
```
Prompts for a name and description interactively.
## Extended Usage
**Set name and description non-interactively:**
```bash theme={null}
alpic playground configure --name "My Server" --description "A helpful MCP server"
```
**Configure a specific environment:**
```bash theme={null}
alpic playground configure --environment-id --name "My Server" --description "A helpful MCP server"
```
## Options
| Flag | Description |
| ------------------- | ------------------------------------------------------------------------ |
| `--environment-id` | The ID of the environment (optional — read from local config if omitted) |
| `--name` | The server name (max 100 characters) |
| `--description` | The server description (max 500 characters) |
| `--non-interactive` | Disable interactive prompts. Requires `--name` and `--description`. |
When running from a directory linked to an Alpic project, `--environment-id` is optional — the CLI reads it from the
local `.alpic` config file.
# playground disable
Source: https://docs.alpic.ai/cli/playground-disable
Disable the playground for an environment
Disables the Alpic playground for a given environment.
## Usage
```bash theme={null}
alpic playground disable
```
Disables the playground for the environment linked to the current directory.
## Extended Usage
**Disable the playground for a specific environment:**
```bash theme={null}
alpic playground disable --environment-id
```
## Options
| Flag | Description |
| ------------------ | ------------------------------------------------------------------------ |
| `--environment-id` | The ID of the environment (optional — read from local config if omitted) |
When running from a directory linked to an Alpic project, `--environment-id` is optional — the CLI reads it from the
local `.alpic` config file.
# playground enable
Source: https://docs.alpic.ai/cli/playground-enable
Enable the playground for an environment
Enables the Alpic playground for a given environment, making it accessible to users.
## Usage
```bash theme={null}
alpic playground enable
```
Enables the playground for the environment linked to the current directory.
## Extended Usage
**Enable the playground for a specific environment:**
```bash theme={null}
alpic playground enable --environment-id
```
## Options
| Flag | Description |
| ------------------ | ------------------------------------------------------------------------ |
| `--environment-id` | The ID of the environment (optional — read from local config if omitted) |
When running from a directory linked to an Alpic project, `--environment-id` is optional — the CLI reads it from the
local `.alpic` config file.
# playground example-prompt add
Source: https://docs.alpic.ai/cli/playground-example-prompt-add
Add an example prompt to the playground
Adds an example prompt to the Alpic playground for an environment. A maximum of 5 example prompts can be added per environment.
## Usage
```bash theme={null}
alpic playground example-prompt add
```
Prompts for a title and prompt text interactively.
## Extended Usage
**Add an example prompt non-interactively:**
```bash theme={null}
alpic playground example-prompt add --title "Hello" --prompt "Say hello to the user"
```
**Add a prompt to a specific environment:**
```bash theme={null}
alpic playground example-prompt add --environment-id --title "Hello" --prompt "Say hello to the user"
```
## Options
| Flag | Description |
| ------------------- | ------------------------------------------------------------------------ |
| `--environment-id` | The ID of the environment (optional — read from local config if omitted) |
| `--title` | The example prompt title (max 100 characters) |
| `--prompt` | The example prompt text (max 500 characters) |
| `--non-interactive` | Disable interactive prompts. Requires `--title` and `--prompt`. |
Each environment supports a maximum of 5 example prompts.
When running from a directory linked to an Alpic project, `--environment-id` is optional — the CLI reads it from the
local `.alpic` config file.
# playground example-prompt list
Source: https://docs.alpic.ai/cli/playground-example-prompt-list
List playground example prompts for an environment
Lists all example prompts configured for the Alpic playground in a given environment.
## Usage
```bash theme={null}
alpic playground example-prompt list
```
Lists example prompts for the environment linked to the current directory.
## Extended Usage
**List example prompts for a specific environment:**
```bash theme={null}
alpic playground example-prompt list --environment-id
```
## Options
| Flag | Description |
| ------------------ | ------------------------------------------------------------------------ |
| `--environment-id` | The ID of the environment (optional — read from local config if omitted) |
When running from a directory linked to an Alpic project, `--environment-id` is optional — the CLI reads it from the
local `.alpic` config file.
# playground example-prompt remove
Source: https://docs.alpic.ai/cli/playground-example-prompt-remove
Remove an example prompt from the playground
Removes an example prompt from the Alpic playground for an environment.
## Usage
```bash theme={null}
alpic playground example-prompt remove
```
Prompts to select an example prompt to remove interactively.
## Extended Usage
**Remove a specific example prompt non-interactively:**
```bash theme={null}
alpic playground example-prompt remove --title "Hello"
```
**Remove a prompt from a specific environment:**
```bash theme={null}
alpic playground example-prompt remove --environment-id --title "Hello"
```
## Options
| Flag | Description |
| ------------------- | ------------------------------------------------------------------------ |
| `--environment-id` | The ID of the environment (optional — read from local config if omitted) |
| `--title` | The title of the example prompt to remove |
| `--non-interactive` | Disable interactive prompts. Requires `--title`. |
When running from a directory linked to an Alpic project, `--environment-id` is optional — the CLI reads it from the
local `.alpic` config file.
# playground headers add
Source: https://docs.alpic.ai/cli/playground-headers-add
Add a header to the playground configuration
Adds a custom HTTP header definition to the Alpic playground for an environment. Headers can be marked as required or secret to control how users provide them when accessing the playground.
## Usage
```bash theme={null}
alpic playground headers add
```
Prompts for header details interactively.
## Extended Usage
**Add a required secret header non-interactively:**
```bash theme={null}
alpic playground headers add --name "X-Api-Key" --description "API key for authentication" --required --secret
```
**Add an optional, visible header:**
```bash theme={null}
alpic playground headers add --name "X-Tenant-Id" --description "Tenant identifier" --no-required --no-secret
```
**Add a header to a specific environment:**
```bash theme={null}
alpic playground headers add --environment-id --name "X-Api-Key" --description "API key for authentication" --required --secret
```
## Options
| Flag | Description |
| ------------------- | ------------------------------------------------------------------------ |
| `--environment-id` | The ID of the environment (optional — read from local config if omitted) |
| `--name` | The header name (max 100 characters) |
| `--description` | The header description (max 200 characters) |
| `--required` | Whether the header is required. Use `--no-required` to make it optional |
| `--secret` | Whether the header value is secret. Use `--no-secret` to keep it visible |
| `--non-interactive` | Disable interactive prompts. Requires `--name` and `--description`. |
When running from a directory linked to an Alpic project, `--environment-id` is optional — the CLI reads it from the
local `.alpic` config file.
# playground headers list
Source: https://docs.alpic.ai/cli/playground-headers-list
List playground headers for an environment
Lists all custom HTTP headers configured for the Alpic playground in a given environment.
## Usage
```bash theme={null}
alpic playground headers list
```
Lists headers for the environment linked to the current directory.
## Extended Usage
**List headers for a specific environment:**
```bash theme={null}
alpic playground headers list --environment-id
```
## Options
| Flag | Description |
| ------------------ | ------------------------------------------------------------------------ |
| `--environment-id` | The ID of the environment (optional — read from local config if omitted) |
When running from a directory linked to an Alpic project, `--environment-id` is optional — the CLI reads it from the
local `.alpic` config file.
# playground headers remove
Source: https://docs.alpic.ai/cli/playground-headers-remove
Remove a playground header
Removes a custom HTTP header from the Alpic playground configuration for an environment.
## Usage
```bash theme={null}
alpic playground headers remove
```
Prompts to select a header to remove interactively.
## Extended Usage
**Remove a specific header non-interactively:**
```bash theme={null}
alpic playground headers remove --name "X-Api-Key"
```
**Remove a header from a specific environment:**
```bash theme={null}
alpic playground headers remove --environment-id --name "X-Api-Key"
```
## Options
| Flag | Description |
| ------------------- | ------------------------------------------------------------------------ |
| `--environment-id` | The ID of the environment (optional — read from local config if omitted) |
| `--name` | The name of the header to remove |
| `--non-interactive` | Disable interactive prompts. Requires `--name`. |
When running from a directory linked to an Alpic project, `--environment-id` is optional — the CLI reads it from the
local `.alpic` config file.
# playground status
Source: https://docs.alpic.ai/cli/playground-status
Show playground configuration for an environment
Displays the current playground configuration for an environment, including enabled state, name, description, example prompts, and headers.
## Usage
```bash theme={null}
alpic playground status
```
Shows playground configuration for the environment linked to the current directory.
## Extended Usage
**Show status for a specific environment:**
```bash theme={null}
alpic playground status --environment-id
```
## Options
| Flag | Description |
| ------------------ | ------------------------------------------------------------------------ |
| `--environment-id` | The ID of the environment (optional — read from local config if omitted) |
When running from a directory linked to an Alpic project, `--environment-id` is optional — the CLI reads it from the
local `.alpic` config file.
# project inspect
Source: https://docs.alpic.ai/cli/project-inspect
Show details of a project and its environments
Displays detailed information about a project, including its ID, name, team, runtime, transport, repository, build settings, and creation date. Also lists all environments belonging to the project.
## Usage
```bash theme={null}
alpic project inspect --project-id
```
Shows details of the specified project and its environments.
## Extended Usage
Inspect a project by name:
```bash theme={null}
alpic project inspect --project-name
```
When no project identifier is provided, you will be prompted to select a project interactively:
```bash theme={null}
alpic project inspect
```
## Options
| Flag | Description | Default |
| ------------------- | ------------------------------------------------------------------------- | ------- |
| `--project-id` | The ID of the project. | — |
| `--project-name` | The name of the project. | — |
| `--non-interactive` | Disable interactive prompts. Requires `--project-id` or `--project-name`. | `false` |
# project list
Source: https://docs.alpic.ai/cli/project-list
List projects
Lists all projects accessible to the authenticated user, showing their ID, name, runtime, source repository, and creation date.
## Usage
```bash theme={null}
alpic project list
```
Lists all projects across all teams.
## Extended Usage
Filter projects by team:
```bash theme={null}
alpic project list --team-id
```
## Options
| Flag | Description | Default |
| ------------------- | --------------------------- | ------- |
| `--team-id` | Filter by team ID | — |
| `--non-interactive` | Disable interactive prompts | `false` |
# publish
Source: https://docs.alpic.ai/cli/publish
Publish your MCP server to the MCP registry
Publish an MCP server project to the MCP registry, making it discoverable to users.
## Usage
```bash theme={null}
alpic publish
```
Prompts for any missing information (domain, title, description) if not already provided.
## Extended Usage
Skip all prompts (useful in CI):
```bash theme={null}
alpic publish --non-interactive --domain my.domain.com --title 'My Server' --description 'Does things'
```
Publish a specific project by ID:
```bash theme={null}
alpic publish --project-id
```
Publish a project by name:
```bash theme={null}
alpic publish --project-name
```
## Options
| Flag | Description |
| ------------------- | ------------------------------------- |
| `--non-interactive` | Skip all prompts |
| `--domain` | Domain to publish for |
| `--title` | Server title (1–100 characters) |
| `--description` | Server description (1–100 characters) |
| `--website-url` | Website URL |
| `--icon-src` | Icon URL |
| `--project-id` | The ID of the project. |
| `--project-name` | The name of the project. |
Authentication is required. Run `alpic login` or set `ALPIC_API_KEY` before publishing.
Learn how to publish your MCP server to the official MCP Registry from your Alpic dashboard.
# team inspect
Source: https://docs.alpic.ai/cli/team-inspect
Show details of a team and its projects
Displays detailed information about a team, including its ID, name, Stripe account status, current usage, and creation date. Also lists all projects belonging to the team.
## Usage
```bash theme={null}
alpic team inspect --team-id
```
Shows details of the specified team and its projects.
When no `--team-id` is provided, you will be prompted to select a team interactively.
```bash theme={null}
alpic team inspect
```
## Request usage
The `Usage` row reports the MCP requests settled during the team's current billing window, against the allowance included in its plan:
```
Usage 4,231 / 200,000 requests (resets in 12 days)
```
Plans with no published allowance report the count alone — `4,231 requests (resets in 12 days)`.
## Options
| Flag | Description | Default |
| ------------------- | --------------------------- | ------- |
| `--team-id` | The ID of the team | — |
| `--non-interactive` | Disable interactive prompts | `false` |
# team list
Source: https://docs.alpic.ai/cli/team-list
List teams for the current user
Lists all teams the authenticated user belongs to, showing their ID, name, and creation date.
## Usage
```bash theme={null}
alpic team list
```
Lists all teams for the current user.
## Options
| Flag | Description | Default |
| ------------------- | --------------------------- | ------- |
| `--non-interactive` | Disable interactive prompts | `false` |
# telemetry disable
Source: https://docs.alpic.ai/cli/telemetry-disable
Disable anonymous CLI telemetry on this machine
Opts this machine out of anonymous CLI usage reporting. The setting is saved to `~/.alpic/config.json`.
## Usage
```bash theme={null}
alpic telemetry disable
```
Disables telemetry and saves the preference locally.
## Options
This subcommand has no flags.
Telemetry can also be disabled without running this command by setting `ALPIC_TELEMETRY_DISABLED=1` or
`DO_NOT_TRACK=1` in your environment. Alpic never collects Personally Identifiable Information (PII).
# telemetry enable
Source: https://docs.alpic.ai/cli/telemetry-enable
Enable anonymous CLI telemetry on this machine
Opts this machine into anonymous CLI usage reporting. The setting is saved to `~/.alpic/config.json`.
## Usage
```bash theme={null}
alpic telemetry enable
```
Enables telemetry and saves the preference locally.
## Options
This subcommand has no flags.
Alpic never collects Personally Identifiable Information (PII). Telemetry only covers anonymous CLI usage patterns
(commands run, outcomes, platform info).
# telemetry status
Source: https://docs.alpic.ai/cli/telemetry-status
Show anonymous CLI telemetry settings for this machine
Prints the current telemetry setting (enabled or disabled) and the anonymous machine ID used for reporting.
## Usage
```bash theme={null}
alpic telemetry status
```
Displays whether telemetry is enabled and the machine ID assigned to this installation.
## Options
This subcommand has no flags.
## Environment Variables
| Variable | Description |
| ---------------------------- | ------------------------------------------------- |
| `ALPIC_TELEMETRY_DISABLED=1` | Disables telemetry regardless of the saved config |
| `DO_NOT_TRACK=1` | Disables telemetry regardless of the saved config |
| `ALPIC_TELEMETRY_DEBUG=1` | Enables debug output for telemetry events |
# tunnel
Source: https://docs.alpic.ai/cli/tunnel
Expose a local server to the internet through an Alpic tunnel
Open a tunnel from your local machine so your MCP server is reachable at a public `https` URL. The tunnel stays open until you press Ctrl+C.
## Usage
```bash theme={null}
alpic tunnel --port 3000
```
Requests to the assigned URL (e.g. `https://superb-marmot-fondue-420.alpic.dev`) are forwarded to `http://localhost:3000` on your machine. Your subdomain is stable and unique to your account — you'll get the same URL every time.
## Playground
Test your local MCP server *in situ* using the [playground](/testing/playground) — a real LLM chat interface. Access it at `/try` on your tunnel URL (e.g. `https://superb-marmot-fondue-420.alpic.dev/try`).
## Options
| Flag | Description |
| -------- | -------------------- |
| `--port` | Local port to tunnel |
Team API keys are not supported for this command. User OAuth authentication is required.
# whoami
Source: https://docs.alpic.ai/cli/whoami
Show the current Alpic identity
Prints information about the currently authenticated identity. Works for both browser-based OAuth sessions and API key authentication.
## Usage
```bash theme={null}
alpic whoami
```
Displays the authenticated user name and email (OAuth) or the team name (API key).
## Extended Usage
```bash theme={null}
# Verify authentication before running a deployment
alpic whoami
alpic deploy
```
If the command reports that you are not logged in, run `alpic login` or set the `ALPIC_API_KEY` environment variable.
## Options
This command has no flags.
# API
Source: https://docs.alpic.ai/developer-tools/api
Alpic REST API
See the full [API reference](/api-reference).
# CLI
Source: https://docs.alpic.ai/developer-tools/cli
Alpic command-line interface
See the full [CLI reference](/cli/overview).
# Alpic MCP Server
Source: https://docs.alpic.ai/developer-tools/mcp
Manage your projects, debug deployment, and check analytics for any MCP server you host with Alpic
### Overview
The Alpic MCP Server is a hosted MCP server at `mcp.alpic.ai` that lets you interact with your Alpic account from any MCP-compatible client. You can browse teams and projects, inspect deployment logs, and review analytics.
### Available Tools
| Tool | Description |
| ----------------- | ------------------------------------------------------------------- |
| `list-teams` | List all teams you belong to. |
| `list-projects` | List all projects for a given team. |
| `deployment-logs` | Fetch deployment logs for a specific project and environment. |
| `get-analytics` | Retrieve analytics data (sessions, requests, errors) for a project. |
#### Project Browser Widget
The `list-projects` tool returns a widget that displays your projects as interactive cards. Each card shows the project name, environment status, and key analytics at a glance. The widget supports both inline and fullscreen display modes so you can quickly scan your projects or dive deeper into a specific one.
### Try it in the Playground
The fastest way to explore the Alpic MCP Server is the built-in [Playground](/distribution/playground) at [`mcp.alpic.ai/try`](https://mcp.alpic.ai/try). It runs a full MCP client in your browser, so you can chat with the tools through a conversational AI interface — no client setup required.
Sign in with your Alpic account and start exploring the tools.
Connection instructions for other MCP clients are detailed directly in the [Playground](/testing/playground).
### Clients supporting MCP Apps
Our MCP Server is using the MCP Apps extension that allows rendering of rich UI interfaces through custom Views. When used with MCP Apps compatible clients, it provides interactive visual components alongside the standard tool responses.
Install the official app directly:
* **ChatGPT** — [Alpic on the ChatGPT app directory](https://chatgpt.com/apps/alpic/asdk_app_6996e5762c508191846b87c57edbbebe)
* **Claude** — [Alpic on the Claude directory](https://claude.ai/directory/3a931cc2-bfb7-463f-a2a5-dea7d1254d1e)
# Domains
Source: https://docs.alpic.ai/distribution/domains
Connect your MCP server to your domain
Alpic supports custom domains (CNAMEs) for your MCP servers, allowing you to use your own domain name instead of the default Alpic one. This feature enables you to maintain your brand identity and provide a more professional experience for your users.
Custom domains are available only on the Pro plan and above.
## Activating Custom CNAMEs
To activate a custom CNAME for your MCP server environment, follow these steps:
In your Alpic dashboard, go to your Project **Settings** tab.
In the Settings page, navigate to the **Domains** section. You will see the list of domains you have added for your
project. Click on **Add** to add a new domain.
Enter your custom domain name and select the environment you want to configure. The CNAME records you need to add to your DNS provider will be displayed in the modal.
Before validating the new domain name in Alpic, configure your DNS provider to point your domain to the Alpic endpoint
as shown above.
Once you have added the DNS records required to link the domain name to Alpic, you can validate it by clicking on **Save & Deploy**.
It usually takes a few minutes for Alpic to validate the domain name addition. You can check the custom domain status in the Domains section as shown below.
Currently, we don't support root domains (e.g., example.com) or wildcard domains (e.g., \*.example.com).
## Routing multiple environments on one domain
A single custom domain can serve **multiple environments of the same project** at once, each mounted under its own path prefix. This lets you advertise multiple versions of your server side by side on the same domain.
It's very handy if you deploy tenant-specific versions of your server (configured by environment variables) or want to version your app for hosts that implement metadata caching (like ChatGPT and Claude).
The mapping is flexible: the same environment can be served under several path prefixes if you want to expose it on more than one route.
### Editing a domain's environment mappings
In your Project **Settings** → **Domains** section, click the **edit** (pencil) button next to the domain you want to configure.
Each row maps one **environment** to one **path prefix**. Leave the path prefix empty to serve that environment on the root path, or enter a prefix such as `v1` to serve it under `/v1`.
Use **Add mapping** to route another environment under a new prefix, or the trash icon to remove a mapping. A domain
must keep at least one mapping.
Click **Save** to apply your changes. Updates are live immediately and may take up to a minute to propagate.
**Path prefix rules**: each prefix must be unique within the domain and be a single URL-safe path segment (letters,
numbers and `-._~`, no slashes, up to 63 characters). The prefixes `mcp`, `oauth2`, `.well-known`, `assets`, `try` and
`health-check` are reserved, because they collide with the routes served on the root path.
This is the recommended way to publish a new version without breaking an existing deployment on the same domain. See
[Versioning](/distribution/versioning#serving-multiple-versions-on-one-domain) for the full workflow.
## Cloudflare users
If your domain is managed by Cloudflare, make sure the CNAME record is set to **DNS only** (gray cloud icon) — not **Proxied** (orange cloud).
When the Cloudflare proxy is enabled, Cloudflare intercepts traffic and replaces your CNAME with its own anycast IPs in public DNS. Alpic's infrastructure (powered by AWS CloudFront) validates the CNAME at registration time, and will fail to verify ownership if the record is proxied.
Adding a domain will fail with a DNS configuration error if the Cloudflare proxy is enabled on the CNAME record. Keep
the record set to **DNS only** while saving — you can re-enable the proxy afterwards only if you intend to route
traffic through Cloudflare rather than directly to Alpic.
# Playground
Source: https://docs.alpic.ai/distribution/playground
Let users try your MCP server directly from their browser with an AI-powered playground.
When you host an MCP server on Alpic, you automatically get a **Playground** accessible at the `/try` path of your server URL. It lets anyone try your server instantly with an AI agent, and provides all the information needed to connect it to their favorite AI client.
Use the Playground to quickly test your app in a production environment, share it with your team for testing and feedback, or showcase your MCP App to external users and guide them through the install process in their favorite AI client.
For example, if your server URL is `https://my-server.alpic.live`, the playground is available at `https://my-server.alpic.live/try`.
If you've configured a [custom domain](/distribution/domains), the playground will be available at
`https://your-custom-domain.com/try`.
Open the Everything App playground to see all features in action — MCP Apps, ChatGPT App widgets, tool execution, and
more.
## What the Playground offers
The playground is a full-featured MCP client running in the browser. It includes:
* **AI-powered chat** — Users can interact with your MCP server through a conversational interface powered by a leading LLM. You can [choose which model](#model-selection) powers the playground.
* **Tool execution** — All your MCP tools are listed in the sidebar and can be called by the AI during the conversation.
* **MCP App & ChatGPT App rendering** — If your tools return [MCP Apps](https://modelcontextprotocol.io/extensions/apps/overview) or [ChatGPT App widgets](https://platform.openai.com/docs/plugins/actions/rendering-output), they are rendered directly in the chat with support for inline, fullscreen, picture-in-picture, and modal display modes.
* **Installation guide** — A built-in modal shows step-by-step instructions to connect your server to 10 different AI clients. Each tab includes copyable code snippets with your server URL pre-filled, so users can get started in seconds.
## Credits
Each team on Alpic receives **\$5 of free credits** to power playground conversations. Credits are shared across all projects within the team.
When credits run out, playground users will see a message asking to contact support. Credits are tracked per team and deducted after each AI response. The amount deducted depends on the [model](#model-selection) in use — more capable models consume credits faster.
## Configuring the Playground
You can customize the playground experience from the **Distribution > Alpic Playground** section of your project in the Alpic dashboard.
### Enable or disable
The playground is **enabled by default** for all environments. You can toggle it off if you don't want to expose the `/try` page.
### Model selection
Choose which AI model powers the playground from the **model dropdown** next to the enable toggle. The selected model handles every conversation in that environment and takes effect immediately.
Available models:
* **Claude Haiku 4.5** *(default)* — Anthropic's fast, cost-efficient model. A good default for quick testing and demos.
* **Claude Sonnet 4.5** — Anthropic's more capable model for complex reasoning and tool use.
* **GPT-5.4** — OpenAI model.
* **GPT-5.5** — OpenAI's latest model.
More capable models produce higher-quality responses but consume [credits](#credits) faster. Claude Haiku 4.5 is the
most economical choice for high-traffic playgrounds.
### Edit Playground settings
Click **Edit Playground** to configure:
* **Server name** — Displayed in the playground header (1–100 characters).
* **Server description** — Shown on the welcome screen and provided to the AI as context so it understands what your server does (1–500 characters).
* **Example prompts** (up to 5) — Quick-start suggestions shown on the welcome screen. Each prompt has a title and a message that gets sent when clicked.
**Example prompts are a great way to onboard new users.** They guide visitors toward the most valuable features of
your server right from the first interaction. Pick prompts that showcase what makes your MCP server unique.
### Custom headers
If your MCP server requires authentication or custom parameters, you can declare **custom headers** in the playground settings:
* **Required headers** — Users must fill them in before they can send messages. A red indicator appears when required headers are missing.
* **Secret headers** — Displayed as password fields so sensitive values are not visible on screen.
# Publishing
Source: https://docs.alpic.ai/distribution/publishing
How to distribute your MCP server to the world
## Publishing to the Official MCP Registry
Alpic supports direct publishing to the [official MCP Registry](https://registry.modelcontextprotocol.io/). This is the recommended way to make your MCP server discoverable in the ecosystem.
Learn how to publish your MCP server directly to the official MCP Registry from your Alpic dashboard.
## Adding your MCP Server to other registries and app stores
In addition to the official MCP Registry, you can submit your MCP server to other popular MCP registries and stores.
| Registry | Type | Submission Process | Link |
| ------------------- | --------- | -------------------------------------- | ------------------------------------------------------------------------------------------------------------------ |
| **Claude** | Official | Approval process by Anthropic | [Submit Form](https://docs.google.com/forms/d/e/1FAIpQLSeafJF2NDI7oYx1r8o0ycivCSVLNq92Mpc1FPxMKSw1CzDkqA/viewform) |
| **OpenAI** | Official | Official OpenAI ChatGPT App Store | Coming soon |
| **VSCode Registry** | Official | Now linked to Official MCP Registry | N/A |
| **Pulse MCP** | Community | Now linked to Official MCP Registry | N/A |
| **Goose Registry** | Official | GitHub discussion submission | [GitHub Discussion](https://github.com/block/goose/discussions/2075) |
| **Cursor** | Official | Direct submission to Cursor's registry | [Submit Form](https://anysphere.typeform.com/to/DX0Pjqgb?typeform-source=docs.cursor.com) |
When submitting to these registries, make sure to provide clear descriptions, installation instructions, and examples
of your MCP server's capabilities.
## Generating MCP Client Install Instructions
You can also distribute your MCP server directly to your users using our [MCP Install Instructions Generator](https://mcp-install-instructions.alpic.cloud/). It allows you to generate a complete tutorial and 1-click install links for the most popular MCP clients.
See how the Kiwi Flight Search MCP server is distributed with complete installation instructions for all major MCP
clients.
The submission process and requirements may vary between registries. Always check the specific requirements for each
registry before submitting.
# Registry
Source: https://docs.alpic.ai/distribution/registry
Publish your MCP server to the official MCP Registry
Alpic allows you to publish your MCP server directly to the official [MCP Registry](https://registry.modelcontextprotocol.io/). Having your server listed on the registry provides several benefits:
* **Discoverability:** Makes it easier for people and AI agents to find your server from a single, authoritative source of the MCP ecosystem.
* **Subregistry distribution:** The registry feeds into subregistries like [GitHub MCP](https://github.com/mcp), [Pulse MCP](https://www.pulsemcp.com/) and more.
* **Standardization:** Your server gets a valid `server.json`, ensuring compatibility across MCP clients.
Published servers follow the MCP Registry [server.json
schema](https://static.modelcontextprotocol.io/schemas/2025-10-17/server.schema.json). Alpic automatically generates
and maintains this file for you.
## Publishing Your Server
In your Alpic dashboard, go to your Project and click on the **Publishing** tab.
The MCP Registry card shows your current publishing status: - If you haven't published yet, you'll see "No published
version yet" - If you've published before, you'll see your latest published version with a link to view it on the MCP
Registry
Click the **New Version** button to open the publishing dialog.
Complete the required fields for your server:
| Field | Required | Description |
| --------------- | -------- | -------------------------------------------------------------------------------- |
| **Title** | Yes | A human-readable name for your server (max 100 characters) |
| **Description** | Yes | A brief description of what your server does (max 100 characters) |
| **Website URL** | No | Link to your server's documentation or homepage |
| **Icon URL** | No | URL to a 200×200 pixel icon image for your server (png format) |
| **Headers** | No | The specific headers required by your MCP server, often used for authentication. |
If your server requires additional headers (e.g. API key authentication), you can configure them in the optional fields section.
For each header, specify:
* **Name:** The HTTP header name (e.g., `Authorization`, `X-API-Key`)
* **Description:** Explain what this header is for (can be displayed to the user when installing your server from the registry). If you are requiring an API key, you can add the URL to get one here.
* **Required:** Whether users must provide this header when installing your server from the registry
* **Secret:** Whether the value should be treated as sensitive data
Review the JSON preview showing your server.json file, then click **Publish** to submit your server to the registry.
Once published, your server will be available on the [MCP Registry](https://registry.modelcontextprotocol.io/). You can click "View on MCP Registry →" to see your server listed there.
Registry publishing is only available for the **Production** environment of your project.
## Additional Notes
### Server Naming Convention
When you publish to the registry, your server is automatically named using the reverse domain notation:
```
{reversed-domain}/{project-name}
```
For example, if your production domain is `mcp.example.com` and your project is named `my-server`, the registry name will be:
```
com.example.mcp/my-server
```
This naming convention ensures uniqueness and clear ownership of servers in the registry.
### Version Management
Alpic uses [semantic versioning](https://semver.org/) (semver) for registry publishing. Each time you publish:
* The version number is automatically incremented (patch version)
* Your first publication starts at version `0.0.1`
* Subsequent publications increment: `0.0.1` → `0.0.2` → `0.0.3`, etc.
Only semantic versioning is supported. If your existing server.json has a non-semver version, you'll need to update it
before publishing.
### Publishing with a Custom Domain
If you have [custom domains](/distribution/domains) configured for your production environment, you can publish your server under any of your verified domains. When you have multiple domains, you'll see tabs in the Publishing section allowing you to manage registry entries for each domain separately.
## Next Steps
Learn about other distribution options beyond the MCP Registry.
# Updating a published app
Source: https://docs.alpic.ai/distribution/versioning
Safely roll out changes to your workloads on ChatGPT and Claude.ai
## Why versioning matters
Most hosts cache some of your MCP workload responses when your submission is accepted.
Knowing which response is cached and which isn't tells you whether a change ships immediately or needs a new submission.
**Cached at submission (frozen until you submit a new version):**
* **`tools/list` response**: tool descriptions, annotations and input/output schemas, on all workloads.
* **`resources/read` response**: the views entrypoint HTML, on MCP apps specifically.
Everything else reaches your server on every user interaction (an actual `tools/call`, for example) and runs your latest deployment.
This split is what makes versioning tricky: say you deploy an update that adds a required input parameter to a tool.
The host still sources its input schema from the cached `tools/list`, so it fires a `tools/call` without the new parameter.
However, your freshly deployed handler now expects it and rejects the request with a validation error.
All calls to this tool now fail, with no possible recovery other than reverting your deployment.
You just broke your production workload.
In order to avoid this scenario, the following table summarizes how to handle the most common operations safely.
All recommended migration paths keep host knowledge of your server and your actual service deployment compatible.
## Operation-specific migration paths
### For all workloads
| Operation | How to prepare | How to release | Notes |
| ----------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------ | -------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Add a new tool | Add the tool | Submit a new version | The tool stays invisible to the host until you submit. |
| Remove a tool | Add `_meta.ui.visibility: ["app"]` on your tool description | Submit a new version | Do not remove your tool altogether. It is still referenced in the cached `tools/list` on hosts and will keep being called by the model until you submit a new version. You can safely remove it after this revised version has been submitted. |
| Add a new param in an existing tool | Add the param in the input schema, but make it optional | Submit a new version | Do not add a required param. Your handler is updated right away with the new requirement, but hosts won't be aware of it until a new release is made and will fail all input validation on your tool. |
| Update a param in an existing tool | Keep changes backward compatible: relax validation, don't tighten it, and avoid renaming. Widen accepted values rather than narrowing them | Submit a new version | Hosts validate inputs against the cached schema. If you tighten validation (e.g. make an optional param required, add new constraints) before resubmitting, the host keeps sending values your handler now rejects. |
| Remove a param in an existing tool | Stop reading the param in your handler, but keep accepting it (leave it optional in the input schema) | Submit a new version | The model keeps sending the param while the host references the cached `tools/list`. Removing it from the input schema before resubmitting can cause input validation to fail. |
### For MCP apps specifically
| Operation | How to prepare | How to release | Notes |
| -------------- | ------------------------------------------------------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Add a new view | Add the view resource and reference it from the relevant tool's `_meta.ui.resourceUri` | Submit a new version | Both `tools/list` and `resources/read` are cached, so the host won't discover the new view until you submit. |
| Update a view | To change view behavior or styling only, ship new assets under stable, deterministic names (see [Asset naming strategy](#asset-naming-strategy)) | Update the assets content. You only need a new submission to change the view entrypoint HTML | The `resources/read` entrypoint HTML is cached. With deterministic asset names you can ship updated assets without resubmitting, because the cached HTML keeps pointing at the same asset URLs. |
| Remove a view | Remove the view and stop referencing it from your tools' `_meta`. Keep the referenced static assets available. | Submit a new version | The view is still referenced by the cached `tools/list` content, and the view content itself is still cached by the host. Removing them from your workload before you submit a new version therefore has no effect on the host. Just make sure the external public assets referenced by the view stay available. |
## Recommended release workflow
We recommend using a **dedicated [environment](/build-deploy/environments)** for each version you submit to a host store.
Because hosts freeze a snapshot of your workload at submission time, a dedicated environment with its dedicated URL gives you a stable, immutable target for each published version:
* Your ongoing development on other environments never affects an already-submitted version.
* You can keep shipping fixes to a submitted version (within the limits described above) without disturbing your next release.
* When you want a clean break, you cut a new release against a new environment and submit it as a new version.
Name your environments after the released version (for example `v1`, `v2`) so it stays obvious which environment backs
which submission on each host.
### Serving multiple versions on one domain
Some hosts, like ChatGPT, require you to **keep the same domain** when you publish a new version of your plugin. In order to be able to use a dedicated environment per version, you should leverage custom domains and advertise multiple versions of your app.
You can map several environments of the same project onto a **single domain using path prefixes**. Each version keeps its own immutable environment (and therefore its own frozen submission snapshot), while sharing one public subdomain. You can satisfy the same-domain requirement without disturbing your existing deployment.
Configure this from your Project **Settings** → **Domains** tab with the domain **edit** button (see [Routing multiple environments on one domain](/distribution/domains#routing-multiple-environments-on-one-domain)).
For example, this is how Alpic routes its own MCP server:
| URL | Environment | Purpose |
| ------------------------- | ------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------- |
| `https://mcp.alpic.ai` | Production (tracking `main`) | The default route we advertise for people installing our server themselves. |
| `https://mcp.alpic.ai/v1` | `v1` (tracking the `v1` branch, patches only) | The version submitted to ChatGPT, kept stable and only receiving patches. |
| `https://mcp.alpic.ai/v2` | `v2` (tracking `main`, will later move to tracking `v2` just before submitting) | The next version under review with OpenAI, carrying breaking `tools/list` and schema changes. |
Keep the route you advertise to end users on the root path (empty prefix), and reserve versioned prefixes (`v1`, `v2`,
…) for host submissions. Point each prefix at the environment whose branch matches that version's release line.
## Asset naming strategy
For MCP apps, the `resources/read` response serves as the views entrypoint HTML.
How your build pipeline bundles and references built assets directly determines your release strategy.
TL;DR: Use **deterministic asset names within a released version and its fixes**. This lets you ship small updates to
your views by simply redeploying, while the host serves the latest asset content from the stable URL it already
cached. Next and previous versions use version-specific asset names and aren't affected.
### Inline assets in HTML
All CSS and JS content is included in the `resources/read` content.
You can't update the view behavior without a new submission.
### Assets bundled separately with unique names per build
Your build pipeline produces content-hashed names that change on every build (`assets-fdsiucv7384.js` on the first build, then `assets-ytiroiu4253.js` on the next one).
The cached entrypoint HTML keeps pointing at the old name, so a new deployment exposes assets the host will never download, and the old assets keep being served.
You can't update the view behavior without a new submission.
### Assets bundled separately with deterministic, stable names
Your build pipeline produces assets with names that stay the same across builds (always `assets.js`).
The cached entrypoint HTML keeps pointing at the same URL, and a redeploy replaces the content served at that URL.
Updated assets ship without resubmission: you control the application lifecycle through deployment, not submission.
However, your changes then impact all existing views across all conversations.
If you use a single environment for all your workload versions, make sure to keep deterministic names only **within**
a single released version scope. You would otherwise risk breaking all existing views. See details below.
Hosts don't just render a view once. To rebuild the conversation as the user scrolls, they persist the view's tool input, output, metadata and — depending on the host's capabilities — its view state in the conversation history. When an old view re-enters the viewport (for example, the user scrolls back in time or reopens an old conversation), the host **re-renders it by re-fetching the asset referenced in that turn's entrypoint HTML**.
If you keep the same asset name across versions, that re-render pulls your **latest** asset code, but feeds it the **old** turn's input/output/metadata. The new asset expects the updated sidecar tool input/output schemas, so it ends up rendering against data shaped for a previous version — breaking or corrupting views in past conversations that were perfectly valid when they were created.
Giving each released version its own asset names keeps every historical turn pinned to the asset it was built against, so old views keep rendering correctly while new views get the new code.
# Deploy from GitLab CI
Source: https://docs.alpic.ai/guides/deploy-from-gitlab-ci
Deploy your MCP server from a GitLab CI/CD pipeline using the Alpic CLI
## Prerequisites
In the [Alpic dashboard](https://app.alpic.ai), open your team and go to **API Keys** in the sidebar. Click **New
API key**, name it, and copy the key.
* **Project ID** : In your project, copy the project ID from **Settings** → **General**, - **Environment ID** : from
the environment's card, in the **Environments** tab.
In your GitLab project, go to **Settings** → **CI/CD** → **Variables** and add `ALPIC_API_KEY` (masked),
`ALPIC_PROJECT_ID`, and `ALPIC_ENVIRONMENT_ID`.
## Configure the pipeline
Add a deploy job to your `.gitlab-ci.yml`:
```yaml .gitlab-ci.yml theme={null}
stages:
- deploy
deploy-alpic:
stage: deploy
image: node:24
script:
- npx alpic@latest deploy
--non-interactive
--project-id "$ALPIC_PROJECT_ID"
--environment-id "$ALPIC_ENVIRONMENT_ID"
rules:
- if: '$CI_COMMIT_BRANCH == "main"'
```
Every push to `main` now deploys to your Alpic environment.
## Deploy to multiple environments
To target several environments (for example staging and production), extend a shared job and set the IDs per environment. Here production is a manual action:
```yaml .gitlab-ci.yml theme={null}
.deploy-alpic:
stage: deploy
image: node:24
script:
- npx alpic@latest deploy
--non-interactive
--project-id "$ALPIC_PROJECT_ID"
--environment-id "$ALPIC_ENVIRONMENT_ID"
deploy-staging:
extends: .deploy-alpic
environment: staging
variables:
ALPIC_PROJECT_ID: $ALPIC_STAGING_PROJECT_ID
ALPIC_ENVIRONMENT_ID: $ALPIC_STAGING_ENV_ID
rules:
- if: '$CI_COMMIT_BRANCH == "main"'
deploy-production:
extends: .deploy-alpic
environment: production
variables:
ALPIC_PROJECT_ID: $ALPIC_PROD_PROJECT_ID
ALPIC_ENVIRONMENT_ID: $ALPIC_PROD_ENV_ID
rules:
- if: '$CI_COMMIT_BRANCH == "main"'
when: manual
allow_failure: true
```
Use `--project-id` and `--environment-id` rather than `--project-name` in CI: names can be changed, IDs are stable.
See the `alpic deploy` reference [right here](/cli/deploy).
# Deploy on Alpic Button
Source: https://docs.alpic.ai/guides/work-with-deploy-button
How to distribute your MCP server template Github repository to Alpic users
## How does it work?
The **Deploy on Alpic** button allows users to deploy a new project through the Alpic Project creation flow, while cloning the source Git repository to their own GitHub namespace.
It can be used to easily add a 1-click Alpic deployment option to your MCP framework, starter template or project.
## Add your own Deploy Button to your repository
Use the snippets below in your Git repositories or your dashboards for users to deploy, replacing the repositoryUrl in the query parameter with your own public git repository URL.
```md Markdown theme={null}
[](https://app.alpic.ai/new/clone?repositoryUrl=https://github.com/alpic-ai/mcp-server-template-nodejs)
```
```html HTML theme={null}
```
```txt URL theme={null}
https://app.alpic.ai/new/clone?repositoryUrl=https://github.com/alpic-ai/mcp-server-template-nodejs
```
The end-result should look like this:
[](https://app.alpic.ai/new/clone?repositoryUrl=https://github.com/alpic-ai/mcp-server-template-nodejs)
Currently, the Deploy on Alpic Button only supports GitHub repositories.
# Welcome to Alpic!
Source: https://docs.alpic.ai/index
Alpic is an all-in-one MCP cloud platform where you can spin up servers, track them with AI-specific analytics, monitor everything, and ship to users without breaking a sweat.
## Getting started
Get your MCP server up and running in minutes.
Follow our step-by-step quickstart guide to deploy your first MCP server.
## Features
Explore our platform functionalities.
See how your MCP servers are performing and get insights on your users.
Capture what users really ask for and collect model feedback directly from your MCP server.
Set up your deployment environments and consult your MCP server logs.
Secure your MCP server with Oauth or API keys.
Use your own domain to access your MCP servers.
Publish your MCP server to the official MCP Registry.
Use Alpic's REST API to programmatically manage your MCP servers, teams, and projects.
## Guides
Read our guides to see how to manage your teams and projects, as well as how to test and distribute your MCP servers.
Learn how to test your MCP server before deploying to production.
Learn how to distribute your MCP server to your users.
Learn how to add a one-click deployment button to your repository.
# Plans
Source: https://docs.alpic.ai/pricing/plans
Alpic pricing plans and what's included
| | Free | Pro | Enterprise |
| ----------------------- | --------------- | --------------- | ---------- |
| **Price** | \$0/mo | \$30/mo | Custom |
| **Requests** | 10,000/mo | 200,000/mo | Custom |
| **Analytics retention** | 7 days | 30 days | Unlimited |
| **Support** | Email & Discord | Email & Discord | Dedicated |
| **Custom domains** | | Yes | Yes |
| **OAuth DCR proxy** | | Yes | Yes |
| **BYO Cloud** | | | Yes |
All plans include Git-native CI/CD, MCP-native observability, and playground distribution.
## Analytics retention
Retention defines how far back your [analytics](/analytics/overview) can go. Time ranges beyond your plan's retention appear locked in the dashboard, and upgrading unlocks longer ranges immediately.
## Overage
Extra requests are billed at **\$150/million requests**.
## Enterprise
For large-scale deployments requiring specialized support. [Contact us](mailto:support@alpic.ai) to learn more.
# Request limit reached
Source: https://docs.alpic.ai/pricing/request-limit-exceeded
The error returned when a team on the Free plan has used every request it includes
`request-limit-exceeded` is the Alpic reason code returned when a team on the Free plan has used every request that plan includes for the current billing period. Paid plans are never refused: requests beyond the included allowance are billed as [overage](/pricing/plans#overage). It is a stable identifier: match on it rather than on the message text, which may be reworded.
This reason code is not returned today. Request limits are counted but not enforced. This page documents the response
shape so clients can handle it before enforcement is turned on.
## The response
The request is refused at admission. It never reaches your MCP server, and it is not counted against your plan.
* **HTTP status**: `402 Payment Required`
* **JSON-RPC error code**: `-32002`
* **Alpic reason code**: `request-limit-exceeded`, under the reserved `alpic/reason` key
```json theme={null}
{
"jsonrpc": "2.0",
"error": {
"code": -32002,
"message": "Request limit reached for the current billing period.",
"data": {
"alpic/reason": "request-limit-exceeded",
"documentation": "https://docs.alpic.ai/pricing/request-limit-exceeded"
}
},
"id": null
}
```
## What counts as a request
Every settled MCP request counts, whatever its outcome — including `initialize`, `tools/list` and `ping`. Because the check runs at admission, a refused request is not added to your count.
The limit applies to the whole team, across every project and environment, for the current billing period. See [Plans](/pricing/plans) for the requests included in each plan and the overage rate.
## Handling it in a client
`initialize` goes through the same check, so a Free-plan team over its limit fails at connection time. Most MCP clients render that as a generic "server unavailable" rather than showing the message, so treat a `402` on connect as a quota problem, not an outage.
## What to do
Check the current period's usage against your allowance on your team's billing page in the [dashboard](https://app.alpic.ai), then move to a paid plan — see [Plans](/pricing/plans). Paid plans keep serving requests beyond their included allowance and bill the extra, so this reason code cannot be returned once you are on one. A new billing period starts your count again from zero.
# Quickstart
Source: https://docs.alpic.ai/quickstart
Deploy your first MCP server on Alpic in minutes!
## Creating your first deployment
1. Go to [app.alpic.ai](https://app.alpic.ai).
2. Click on **Sign in with GitHub**.
3. Authorize **Alpic AI** to access your GitHub account.
1. Click on **New Project**.
2. You'll be asked to add the Alpic AI app to your GitHub organization. This connection enables seamless sync between deployments and your git workflow.
3. Click on **Add Github Account**.
4. Select the organization and repositories you want Alpic to access.
5. Click on **Install**.
You can link several organizations to your Alpic account.
1. Choose the organization and repository containing your MCP server.
2. Click **Import**.
If you don't have an existing MCP server repository yet, you can get started with one of our templates: - [Typescript
template](https://app.alpic.ai/new/clone?repositoryUrl=https://github.com/alpic-ai/mcp-server-template-nodejs). -
[Python
template](https://app.alpic.ai/new/clone?repositoryUrl=https://github.com/alpic-ai/mcp-server-template-python). -
[ChatGPT App template](https://app.alpic.ai/new/clone?repositoryUrl=https://github.com/alpic-ai/apps-sdk-template).
1. Choose which branch to sync with your main production environment. Alpic deploys a new version of your MCP server each time you push changes to this branch.
2. Specify the environment variables your MCP server needs. You can edit these later in your project settings.
3. Confirm the build commands used to build your MCP server. We automatically detect your MCP framework and build commands from your repository metadata, but you can customize them if needed:
* **Build command:** Command to build your MCP server.
* **Output directory:** Directory where the built files are located.
* **Install command:** Dependencies installation command.
4. Click **Deploy** and you're done! 🚀
## Your MCP server is deployed! What's next?
Now that your MCP server is deployed, you can start using and testing it in your favorite MCP client. To learn more about how to test and distribute your MCP server, check out our guides:
Learn how to host your ChatGPT App on Alpic.
Test and debug your MCP server with the MCP inspector and connect it different MCP clients.
Learn how to distribute your MCP server to your users.
Learn about the Alpic internal endpoints and URLs exposed by your server.
Also take a look at our additional features, like Authentication, Analytics, Custom Domains, and more.
Track how your MCP servers are performing and get insights on your users.
Learn what users really ask for and collect model feedback directly from your MCP server.
Enable secure authentication with Dynamic Client Registration.
Set up staging and development environments to preview and test new versions of your server.
Use your own domain to access your MCP servers.
Make your server discoverable in the official MCP ecosystem.
Use Alpic's REST API to programmatically manage your MCP servers, teams, and projects.
Learn how to add a one-click deployment button to your repository.
# Dynamic Client Registration Proxy
Source: https://docs.alpic.ai/secure/auth/dcr-proxy
Understand how Alpic's in-house DCR proxy works and when you should use it
## What is Alpic's DCR proxy?
MCP only support OAuth as an official authentication mechanism to protect servers. While most features required to make MCP work lives within OAuth 2.O, the protocol require a specific feature of OAuth 2.1: [Dynamic Client Registration (DCR)](https://datatracker.ietf.org/doc/html/rfc7591).
Many identity providers (Auth0, Google, etc.) don't natively support DCR. Alpic bridges this gap with a **DCR proxy**: Alpic exposes a set of OAuth endpoints on top of your provider. When an MCP client connects to your server, it goes through Alpic endpoints which in turn use the OAuth metadata from your server. Alpic issues client credentials as part of the DCR spec. Only a single set of client credentials are in effective use from your auth provider perspective. We handle the complexity of keeping track of all OAuth client issued to use your MCP server. All those clients are stored in Alpic in a **client pool**.
## How it works
1. You create a **client pool** in your environment's authentication settings, providing the client ID, secret, and scopes of a single OAuth client you created on your identity provider.
2. Alpic advertises a `registration_endpoint` in your server's OAuth discovery metadata.
3. When an MCP client connects, it calls the registration endpoint and receives a unique `client_id` and `client_secret`.
4. The MCP client caches these credentials locally and uses them for all subsequent OAuth flows against your identity provider.
5. Under the hood, Alpic proxies all token requests through the single upstream client you configured.
## Managing your client pool
You can **create**, **update**, or **delete** your client pool from the **Settings → Authentication** tab of your project.
* **Create** — set up the DCR proxy for the first time by providing your upstream client credentials. In order to have the option to setup your DCR proxy, your MCP server must reference itself as the issuer and lack a registration\_endpoint.s
* **Update** — change the client ID, secret, or scopes. This is safe for existing MCP clients as their issued credentials remain valid.
* **Delete** — remove the client pool entirely. This is a destructive action with consequences detailed below.
## Impact of deleting the client pool
Deleting a client pool is an irreversible operation that will break existing MCP client connections.
When you delete a client pool:
* **All dynamic client registrations are permanently removed.** Every `client_id` / `client_secret` pair that was issued to MCP clients ceases to exist on Alpic's side.
* **MCP clients still hold their cached credentials.** The MCP OAuth specification mandates that clients cache their registration credentials locally. Alpic cannot invalidate or revoke those cached values.
* **Reconnection attempts will fail.** When an MCP client tries to reconnect using its cached credentials, the token exchange will fail because Alpic no longer recognizes the client.
* **The registration endpoint disappears.** Your server's OAuth discovery metadata will no longer advertise a `registration_endpoint`, so new clients cannot register via DCR until a new pool is created.
### When is it safe to delete?
| Scenario | Safe? | Why |
| ------------------------------------------------------ | ----- | ------------------------------------------------------------------------ |
| Your server has never been used by an MCP client | ✅ | No credentials have been issued |
| You are decommissioning the server entirely | ✅ | No future connections are expected |
| You are switching to an IdP that natively supports DCR | ✅ | You'll remove the proxy in favor of your IdP's own registration endpoint |
| Your server has active users | ❌ | Existing clients will break — update the pool instead |
If you need to rotate credentials or change identity providers, **update your client pool** rather than deleting it. Updating preserves all existing dynamic client registrations — only the upstream OAuth client credentials change. MCP clients will continue authenticating without interruption.
# OAuth Providers
Source: https://docs.alpic.ai/secure/auth/oauth-providers
List of popular Identity Providers (IdP) and their corresponding configuration endpoints.
Here is a list of popular Identity Providers (IdP) and their corresponding configuration endpoints.
Those endpoints are public and contain information that you'll need to properly configure your MCP server OAuth protection.
OpenID Connect (OIDC) is a superset of OAuth 2.0 - if a platform is OIDC compliant, it's OAuth 2.0 compliant.
# OpenID Connect (OIDC) compliant Providers
These providers are popular IdPs compliant with OpenID Connect.
You can use them as identity pools for your MCP server (meaning you don't have to own yourself a database of users, you can instead simply gate access to your MCP server with these identity providers).
None of these providers have Dynamic Client Registration enabled. You'll need to use [Alpic DCR
proxy](/secure/auth/oauth-setup#using-an-oauth-2-0-compatible-identity-provider-idp) in order to use them as valid
identity providers for your MCP server.
| Identity Provider | OpenID Connect Configuration Well-Known Endpoint |
| :---------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Apple | [https://account.apple.com/.well-known/openid-configuration](https://account.apple.com/.well-known/openid-configuration) |
| Coinbase | [https://login.coinbase.com/.well-known/openid-configuration](https://login.coinbase.com/.well-known/openid-configuration) |
| Discord | [https://discord.com/.well-known/openid-configuration](https://discord.com/.well-known/openid-configuration) |
| Dropbox | [https://www.dropbox.com/.well-known/openid-configuration](https://www.dropbox.com/.well-known/openid-configuration) |
| Facebook | [https://www.facebook.com/.well-known/openid-configuration](https://www.facebook.com/.well-known/openid-configuration) |
| Github | [https://github.com/login/oauth/.well-known/openid-configuration](https://github.com/login/oauth/.well-known/openid-configuration) |
| GitLab | [https://gitlab.com/.well-known/openid-configuration](https://gitlab.com/.well-known/openid-configuration) |
| Google | [https://accounts.google.com/.well-known/openid-configuration](https://accounts.google.com/.well-known/openid-configuration) |
| Hugging Face | [https://huggingface.co/.well-known/openid-configuration](https://huggingface.co/.well-known/openid-configuration) |
| Kakao | [https://kauth.kakao.com/.well-known/openid-configuration](https://kauth.kakao.com/.well-known/openid-configuration) |
| Line | [https://access.line.me/.well-known/openid-configuration](https://access.line.me/.well-known/openid-configuration) |
| LinkedIn | [https://www.linkedin.com/oauth/.well-known/openid-configuration](https://www.linkedin.com/oauth/.well-known/openid-configuration) |
| Microsoft | [https://login.microsoftonline.com/common/v2.0/.well-known/openid-configuration](https://login.microsoftonline.com/common/v2.0/.well-known/openid-configuration) |
| Salesforce | [https://login.salesforce.com/.well-known/openid-configuration](https://login.salesforce.com/.well-known/openid-configuration) |
| Slack | [https://slack.com/.well-known/openid-configuration](https://slack.com/.well-known/openid-configuration) |
| Spotify | [https://accounts.spotify.com/.well-known/openid-configuration](https://accounts.spotify.com/.well-known/openid-configuration) |
| Twitch | [https://id.twitch.tv/oauth2/.well-known/openid-configuration](https://id.twitch.tv/oauth2/.well-known/openid-configuration) |
| Xero | [https://identity.xero.com/.well-known/openid-configuration](https://identity.xero.com/.well-known/openid-configuration) |
# Identity Management Platforms
You'll find below the most popular IdPs compliant with OAuth 2.1. You can use them to provision your own user pools for your MCP server.
| Identity Management Platform | OpenID Connect Configuration Well-Known Endpoint |
| :---------------------------------------------------------: | :------------------------------------------------------------------------------------------- |
| Auth0 | `https://{tenant}.us.auth0.com/.well-known/openid-configuration` |
| Amazon Cognito | `https://cognito-idp.{region}.amazonaws.com/{user-pool-id}/.well-known/openid-configuration` |
| Clerk | `https://{tenant}.clerk.accounts.dev/.well-known/openid-configuration` |
| Google Identity Platform (formerly Firebase Authentication) | `https://securetoken.google.com/{tenant}/.well-known/openid-configuration` |
| Logto | `https://{tenant}.logto.app/.well-known/openid-configuration` |
| Microsoft Entra ID (formerly Azure AD) | `https://login.microsoftonline.com/{tenant}/v2.0/.well-known/openid-configuration` |
| Okta | `https://{tenant}.okta.com/.well-known/openid-configuration` |
| Stytch | `https://{tenant}.customers.stytch.com/.well-known/openid-configuration` |
| WorkOS | `https://{tenant}.authkit.app/.well-known/openid-configuration` |
You might want to try `https://{your - domain}/.well-known/openid-configuration` instead of the provider specific URL
if a custom domain has been setup on your Identity Management Platform.
# Other Identity Providers
You can use any IdP to configure authorization on your MCP server.
Use the login page domain to discover `/.well-known/oauth-authorization-server` endpoint.
For exemple, you can login to `Box` using `https://account.box.com/login`. Box's OAuth Authorization Server metadata endpoint is `https://account.box.com/.well-known/oauth-authorization-server`
# OAuth authentication
Source: https://docs.alpic.ai/secure/auth/oauth-setup
Configure OAuth authentication for your MCP servers on Alpic
To add OAuth authentication to your MCP server, you need to have an Identity Provider (IdP) that manages your users identities.
Depending on your IdP, you can be in one of the 4 following cases:
Your IdP is OAuth 2.1 compatible and implements Dynamic Client Registration (DCR)
Your IdP is OAuth 2.0 compatible but doesn't implement DCR
Your IdP doesn't provide OAuth metadata endpoints
You don't have an IdP yet as you are starting from scratch
### Using an OAuth 2.1 compatible Identity Providers (IdP)
If you're already using an OAuth 2.1 identity provider with Dynamic Client Registration (DCR), you simply need to configure your MCP server to advertise your IdP through OAuth metadata endpoints.
The easiest way to do so is to rely on existing SDK helpers to provide such configuration on your server:
```ts MCP Typescript SDK theme={null}
import { requireBearerAuth } from "@modelcontextprotocol/sdk/server/auth/middleware/bearerAuth.js";
import { mcpAuthMetadataRouter } from "@modelcontextprotocol/sdk/server/auth/router.js";
//...
app.use(
mcpAuthMetadataRouter({
oauthMetadata: {
authorization_endpoint: "https://my-idp.com/oauth2/authorize",
token_endpoint: "https://my-idp.com/oauth2/token",
registration_endpoint: "https://my-idp.com/oauth2/register",
response_types_supported: ["code"],
code_challenge_methods_supported: ["S256"],
token_endpoint_auth_methods_supported: ["client_secret_post"],
issuer: "https://my-idp.com",
},
resourceServerUrl: new URL("http://localhost:3000"),
}),
);
const authMiddleware = requireBearerAuth({
verifier: {
verifyAccessToken: async (token) => {
// Generic jwtDecode for JWT auth token or any IDP custom verification method
return { token, clientId: "clientId", scopes: [], expiresAt: Date.now() / 1000 + 600 };
},
},
});
app.post("/mcp", authMiddleware, async (req: Request, res: Response) => {});
app.get("/mcp", authMiddleware, async (req: Request, res: Response) => {});
app.delete("/mcp", authMiddleware, async (req: Request, res: Response) => {});
app.listen(3000);
```
```python FastMCP theme={null}
from typing import Any
from fastmcp.server.auth import AccessToken, RemoteAuthProvider, TokenVerifier
from mcp.server.auth.handlers.metadata import MetadataHandler
from mcp.server.auth.routes import cors_middleware
from mcp.shared.auth import OAuthMetadata
from starlette.routing import Route
from pydantic import AnyHttpUrl
class CustomTokenVerifier(TokenVerifier):
"""Token verifier for the custom auth provider."""
async def verify_token(self, token: str) -> AccessToken | None:
"""Generic jwtDecode for JWT auth token or any IDP custom verification method"""
return AccessToken(token=token, client_id="clientId", scopes=[])
class CustomAuthProvider(RemoteAuthProvider):
"""Authentication provider for MCP servers that act both as an authorization server and a resource server.
"""
base_url: AnyHttpUrl
def __init__(
self,
base_url: AnyHttpUrl | str,
):
"""Initialize the custom auth provider.
Args:
token_verifier: TokenVerifier instance for token validation
base_url: The base URL of this server
"""
super().__init__(
token_verifier=CustomTokenVerifier(),
base_url=base_url,
authorization_servers=[base_url],
)
def get_routes(self, mcp_path: str | None = None, mcp_endpoint: Any | None = None) -> list[Route]:
routes = super().get_routes(mcp_path, mcp_endpoint)
routes.append(Route(
"/.well-known/oauth-authorization-server",
endpoint=cors_middleware(
MetadataHandler(
OAuthMetadata(
issuer=AnyHttpUrl("http://localhost:8000"),
authorization_endpoint=AnyHttpUrl("https://my-idp.com/oauth2/authorize"),
token_endpoint=AnyHttpUrl("https://my-idp.com/oauth2/token"),
registration_endpoint=AnyHttpUrl("https://my-idp.com/oauth2/register"),
response_types_supported=["code"],
code_challenge_methods_supported=["S256"],
token_endpoint_auth_methods_supported=["client_secret_post"],
)
).handle,
["GET", "OPTIONS"],
),
methods=["GET", "OPTIONS"],
))
return routes
auth = CustomAuthProvider(base_url="http://localhost:8000")
mcp = FastMCP("My MCP Server", auth=auth)
```
Configuring your MCP server to use an existing Identity Provider (IdP) with OAuth requires knowledge of a the
different oauth endpoint URLs. We have curated a list of endpoints for the most used IdPs
[here](/secure/auth/oauth-providers).
After deploying a new version of your MCP server on Alpic with such a configuration, you should see your server as **Protected** in the **Settings** tab of your project page. This will confirm that your MCP server is protected by OAuth.
We made Alpic to be fully compatible with your local server code. If your authentication code runs locally, it should
work on Alpic. In the example above, you should keep the localhost URL for the resource server when your deploy to
Alpic.
If you want to use your deployed MCP server URL as the OAuth server instead of `localhost`, the `ALPIC_HOST`
environment variable was conceived for this use case. It's automatically set at both build and runtime, allowing Alpic
to treat your deployed host as an internal host for OAuth metadata discovery. Learn more about [system environment
variables](/build-deploy/env-variables).
### Using an OAuth 2.0 compatible Identity Provider (IdP) with no DCR
If you're not using an OAuth 2.1 IdP with DCR, you can rely on [**Alpic's Dynamic Client Registration proxy**](./dcr-proxy) to handle the complexity associated with multiple OAuth clients registering to use your IdP.
In order to leverage Alpic's DCR proxy feature, you should configure your MCP server to advertise your IdP through OAuth metadata endpoints without mentioning a `registration_endpoint` property.
The easiest way to do so is to rely on existing SDK helpers to provide such configuration on your server:
```ts MCP Typescript SDK theme={null}
import { requireBearerAuth } from "@modelcontextprotocol/sdk/server/auth/middleware/bearerAuth.js";
import { mcpAuthMetadataRouter } from "@modelcontextprotocol/sdk/server/auth/router.js";
//...
app.use(
mcpAuthMetadataRouter({
oauthMetadata: {
authorization_endpoint: "https://my-idp.com/oauth2/authorize",
token_endpoint: "https://my-idp.com/oauth2/token",
response_types_supported: ["code"],
code_challenge_methods_supported: ["S256"],
token_endpoint_auth_methods_supported: ["client_secret_post"],
issuer: "https://my-idp.com",
},
resourceServerUrl: new URL("http://localhost:3000"),
}),
);
const authMiddleware = requireBearerAuth({
verifier: {
verifyAccessToken: async (token) => {
// Generic jwtDecode for JWT auth token or any IDP custom verification method
return { token, clientId: "clientId", scopes: [], expiresAt: Date.now() / 1000 + 600 };
},
},
});
app.post("/mcp", authMiddleware, async (req: Request, res: Response) => {});
app.get("/mcp", authMiddleware, async (req: Request, res: Response) => {});
app.delete("/mcp", authMiddleware, async (req: Request, res: Response) => {});
app.listen(3000);
```
```python FastMCP theme={null}
from typing import Any
from fastmcp.server.auth import AccessToken, RemoteAuthProvider, TokenVerifier
from mcp.server.auth.handlers.metadata import MetadataHandler
from mcp.server.auth.routes import cors_middleware
from mcp.shared.auth import OAuthMetadata
from starlette.routing import Route
from pydantic import AnyHttpUrl
class CustomTokenVerifier(TokenVerifier):
"""Token verifier for the custom auth provider."""
async def verify_token(self, token: str) -> AccessToken | None:
"""Generic jwtDecode for JWT auth token or any IDP custom verification method"""
return AccessToken(token=token, client_id="clientId", scopes=[])
class CustomAuthProvider(RemoteAuthProvider):
"""Authentication provider for MCP servers that act both as an authorization server and a resource server.
"""
base_url: AnyHttpUrl
def __init__(
self,
base_url: AnyHttpUrl | str,
):
"""Initialize the custom auth provider.
Args:
token_verifier: TokenVerifier instance for token validation
base_url: The base URL of this server
"""
super().__init__(
token_verifier=CustomTokenVerifier(),
base_url=base_url,
authorization_servers=[base_url],
)
def get_routes(self, mcp_path: str | None = None, mcp_endpoint: Any | None = None) -> list[Route]:
routes = super().get_routes(mcp_path, mcp_endpoint)
routes.append(Route(
"/.well-known/oauth-authorization-server",
endpoint=cors_middleware(
MetadataHandler(
OAuthMetadata(
issuer=AnyHttpUrl("http://localhost:8000"),
authorization_endpoint=AnyHttpUrl("https://my-idp.com/oauth2/authorize"),
token_endpoint=AnyHttpUrl("https://my-idp.com/oauth2/token"),
response_types_supported=["code"],
code_challenge_methods_supported=["S256"],
token_endpoint_auth_methods_supported=["client_secret_post"],
)
).handle,
["GET", "OPTIONS"],
),
methods=["GET", "OPTIONS"],
))
return routes
auth = CustomAuthProvider(base_url="http://localhost:8000")
mcp = FastMCP("My MCP Server", auth=auth)
```
After deploying a new version of your MCP server on Alpic with such a configuration, check that your server is detected as **Protected** in the **Settings** tab of your project page.
If that's the case, you will see an option to **activate Alpic DCR**.
In order to activate DCR, you should create a single OAuth client on your IdP. This client will be the one proxied by Alpic for every request users make to identify on your MCP server. Make sure to choose a generic enough name in case it's displayed during the authentication process.
The callback URL to use for this client is specified by Alpic when activating DCR.
Please fill-in the activation form with newly created OAuth **client ID**, **client secret** and **scopes** to complete the DCR proxy setup.
### Using and IdP without OAuth support
In the case you have an internal identity management system, or are using an identity management system that doesn't support Oauth, you can use providers that take care of Oauth and DCR flow for you while keeping your identity system. Here are some providers supporting the MCP use-case:
* [Stytch Standalone Apps](https://stytch.com/docs/guides/connected-apps/overview)
* [WorkOS Connect](https://workos.com/docs/authkit/connect/standalone)
Once you have configured your MCP Oauth 2.1 workflow with that provider, you can use the [examples above](#using-an-oauth-21-compatible-identity-providers-idp) to integrate it into your MCP server.
### Building from scratch
If you don't have any identity system, you can build yours, or rely on one of the many Identiy Managment Systems out there.
You can check our OAuth [providers list](/secure/auth/oauth-providers#identity-management-platforms) to see which of them is supporting OAuth 2.1 with DCR or not.
# Overview
Source: https://docs.alpic.ai/secure/auth/overview
Secure your MCP servers with flexible authentication options on Alpic
Alpic supports many different authentication options for your MCP servers, from simple public access to enterprise-grade OAuth integration.
By default, servers deployed on Alpic are **public** and accessible to anyone with the server URL.
## Authentication Options
If you want to protect your server with authentication, you have two main options:
Integrate with identity providers using OAuth.
Protect your server with API keys via custom headers.
Alpic itself is not an authentication provider or IdP, but is compatible with all authentication options above. You
implement authentication in your MCP server code, and Alpic will host and serve your protected server.
# API Key authentication
Source: https://docs.alpic.ai/secure/auth/token-bearer-setup
Protect your MCP server with API Keys
Alpic also supports API key Authentication via the *x-api-key* header.
You can get the value of the api key headers in your tool callback:
```ts Typescript theme={null}
server.tool(
"greet",
"A simple greeting tool",
{
name: z.string().describe("Name to greet"),
},
async (
args: { name: string },
extra: RequestHandlerExtra,
): Promise => {
const apiKey = extra.requestInfo?.headers["x-api-key"];
if (!apiKey) {
return {
content: [
isError: true,
{
type: "text",
text: `You need to have a valid api key to use this tool.`,
},
],
};
}
const yourApiClient = new yourApiClient(apiKey);
const yourApiResponse = await yourApiClient.greet(name);
return {
content: [
{
type: "text",
text: `${yourApiResponse}`,
},
],
};
},
);
```
```python Python theme={null}
from mcp.server.fastmcp import Context, FastMCP
from pydantic import Field
mcp = FastMCP("Echo Server", stateless_http=True)
@mcp.tool(
title="Echo Tool",
description="Echo the input text",
)
async def echo(ctx: Context, text: str = Field(description="The text to echo")) -> str:
api_key = ctx.request_context.request.get("x-api-key")
if not api_key:
raise ValueError("API key is required")
your_api_client = YourApiClient(api_key)
your_api_response = await your_api_client.echo(text)
return your_api_response
```
You can then instruct your users to add this custom headers when adding your MCP server to their MCP Client.
```json Cursor theme={null}
{
"mcpServers": {
"yourMcpServer": {
"url": "https://your-mcp-server.alpic.live",
"headers": {
"x-api-key": ""
}
}
}
}
```
```json Claude Desktop theme={null}
{
"mcpServers": {
"yourMcpServer": {
"command": "npx",
"args": ["mcp-remote", "https://your-mcp-server.alpic.live", "--header", "x-api-key: "]
}
}
}
```
```bash VSCode theme={null}
code --add-mcp '{"type":"http","name":"yourMcpServer","version":"0.0.1","description":"your amazing server","url":"https://your-mcp-server.alpic.live","author":"You","categories":["mcp"],"headers":{"x-api-key":""}}'
```
```bash Claude Code theme={null}
claude mcp add --transport http yourMcpServer https://your-mcp-server.alpic.live \
--header "x-api-key: "
```
```md Goose theme={null}
Go to Extensions
Click on Add custom extension
Fill the following information:
- Extension Name: yourMcpServer
- Type: Streamable HTTP
- Description: Your amazing MCP server
- Endpoint: https://your-mcp-server.alpic.live
Enter authentication headers:
Scroll down to the Request Headers section
Click the + Add button
Enter Header name: x-api-key
Enter Value:
```
# Fixed Outbound IP
Source: https://docs.alpic.ai/secure/fixed-outbound-ip
Route your MCP server's outbound traffic through fixed IP addresses
If your MCP server calls external APIs that require IP whitelisting, you can enable fixed outbound IPs so all outbound traffic uses predictable addresses.
Available on the **Enterprise plan** only.
## Setup
In your project **Settings**, toggle the **Fixed Outbound IP** switch. This may take a few minutes.
The current IP list is always available at [`https://assets.alpic.ai/ip-ranges.json`](https://assets.alpic.ai/ip-ranges.json).
The IPs are stable and won't change without advance notice.
# IP Whitelisting
Source: https://docs.alpic.ai/secure/ip-whitelisting
Restrict access to your MCP server by IP address or MCP client
IP whitelisting lets you control which IP addresses can reach your MCP server. When enabled, only requests from approved IPs or MCP clients are allowed — all others receive a `403 Forbidden` response.
This is useful when you want to limit access to known clients, such as ChatGPT or Claude, or restrict usage to your corporate network.
IP whitelisting is configured **per environment**. Make sure you configure it for the right environment (e.g.,
production, staging).
## Platform Selection
For common platforms, Alpic maintains up-to-date IP lists so you don't have to manage them manually. Simply select the platforms you want to allow.
### Conversational platforms
Select the MCP clients whose users reach your server directly:
| Platform | IP Source |
| ----------- | ------------------------------------------------------------------------------------------------------------------------------------ |
| **ChatGPT** | Fetched automatically from [OpenAI's published IP list](https://openai.com/chatgpt-connectors.json), refreshed every 15 minutes |
| **Claude** | Based on [Anthropic's published IP addresses](https://platform.claude.com/docs/en/api/ip-addresses), updated with each Alpic release |
ChatGPT IPs are fetched dynamically and remain up to date automatically. Claude IPs are updated with Alpic releases, so if Anthropic changes their egress IP ranges, there may be a short delay before the changes are reflected.
### WAF / CDN platforms
If your MCP server is fronted by a WAF or CDN, trust that provider's egress IP ranges so requests forwarded through it aren't blocked:
| Platform | IP Source |
| -------------- | --------------------------------------------------------------------------------------------------------------------------- |
| **Akamai** | Fetched from [Akamai's published edge CIDR lists](https://techdocs.akamai.com/origin-ip-acl/docs/update-your-origin-server) |
| **Cloudflare** | Fetched from [Cloudflare's IP ranges API](https://api.cloudflare.com/client/v4/ips) |
| **Fastly** | Fetched from [Fastly's public IP list](https://api.fastly.com/public-ip-list) |
## Custom IPs
You can also add up to **20 custom IP addresses or CIDR ranges** per environment. Supported formats:
* IPv4 addresses (e.g., `203.0.113.10`)
* IPv6 addresses (e.g., `2001:db8::1`)
* IPv4 CIDR ranges (e.g., `203.0.113.0/24`)
* IPv6 CIDR ranges (e.g., `2001:db8::/32`)
Custom IPs and platform selections work together — a request is allowed if it matches **either** list.
## Setup
In your Alpic dashboard, open your project and go to the **Distribution** tab, then select the **Trusted IPs** section.
Toggle the IP whitelist switch to enable the feature.
Check the platforms you want to allow — conversational clients (ChatGPT, Claude) and/or WAF/CDN providers (Akamai,
Cloudflare, Fastly) — and optionally add custom IP addresses or CIDR ranges.
Click **Save** to apply the changes. The restrictions take effect immediately for new requests.
IPv4-mapped IPv6 addresses (e.g., `::ffff:192.168.1.1`) are automatically normalized, so you only need to add the IPv4
form.
# Beacon
Source: https://docs.alpic.ai/testing/beacon
Audit your MCP server and MCP Apps for spec compliance and AI client compatibility.
Beacon is Alpic's automated audit for remote MCP servers and [MCP Apps](https://modelcontextprotocol.io/extensions/apps/overview). Point it at a URL and it reports whether the server is ready to ship to ChatGPT and Claude.ai — including protocol conformance, tool and resource metadata, app rendering in each client, and how the server handles unexpected inputs.
Beacon doesn't just static-check your manifest: it **launches your server inside a real ChatGPT and Claude.ai conversation in a headless browser**, triggers a tool that exposes an MCP App, and asserts that the app actually renders end-to-end. Screenshots from each run are attached to the report so you can see what your users will see.
Use Beacon before publishing a new server, after every significant change, or as a CI gate on your deployments.
## What Beacon checks
Beacon evaluates your server and any MCP Apps it exposes against the specifications and platform requirements that ChatGPT and Claude.ai apply when reviewing an app.
**MCP server specs** — protocol conformance and tool/resource shape:
* [MCP specification — tools](https://modelcontextprotocol.io/specification/2025-11-25/server/tools)
* [MCP specification — resources](https://modelcontextprotocol.io/specification/2025-11-25/server/resources)
**MCP Apps specs** — requirements that apply once your server ships an MCP App (a view resource rendered by the client):
* [Apps SDK reference](https://developers.openai.com/apps-sdk/reference/) — ChatGPT Apps requirements (metadata, CSP, widget descriptions)
* [Remote MCP Server Submission Guide](https://support.claude.com/en/articles/12922490-remote-mcp-server-submission-guide) — Claude.ai-specific requirements for remote MCP Apps
Every check carries a severity. **Errors** must be fixed before a platform will accept the server; **warnings** should be fixed before submission; **info** is surfaced for awareness. The report also outputs a per-platform readiness verdict (`ChatGPT` / `Claude.ai`) derived from the checks that apply to each platform.
Beacon currently only supports unauthenticated MCP servers. If your server responds with `HTTP 401`, Beacon reports
that authentication is required and skips the rest of the checks. Support for authenticated audits is on the roadmap.
## Running an audit
### From the dashboard
Open your team's **Beacon** tab, paste an HTTPS URL, and hit **Run**. The page streams progress as each collector finishes and opens a detailed report when the audit completes. Past audits for the team are listed so you can revisit or compare them.
### From the CLI
Use [`alpic audit`](/cli/audit) to run Beacon from your terminal or CI pipeline:
```bash theme={null}
alpic audit --url https://my-server.example.com/mcp
```
When run inside a linked project, Beacon targets the project's deployed MCP URL automatically. Pass `--json` to get the full report for further processing in CI.
The CLI skips the end-to-end app rendering category by default because it takes several minutes per platform. Run the
full audit (including live app rendering) from the dashboard.
## Reading the report
The report groups results by severity and surfaces:
* A **readiness badge** for ChatGPT and Claude.ai — green when no blocking errors remain and, for servers that ship an MCP App, at least one app check succeeded for that platform.
* A **list of issues** with a short message, affected tool/resource, and a one-line hint explaining how to fix it.
* **App screenshots** captured from the real ChatGPT and Claude.ai browser sessions Beacon ran, so you can visually confirm what your MCP App looks like in each client.
* A **Fix with AI** action that packages the failing checks into a prompt you can paste into Claude Code, Cursor, or any other coding agent. The prompt references the same specs listed above so the agent can reason against the source of truth.
If a check is `skip`ped, it's because its required artifact wasn't available — for example, a tool-level check has no tools to run against, or an app-rendering check has no MCP App resources to render. Skips are informational and never block platform readiness.
# Playground
Source: https://docs.alpic.ai/testing/playground
How to access and test your MCP Server
Once your deployment on Alpic is successful, you get a remote URL to connect to your MCP server.
### Try it instantly with the Playground
Every server deployed on Alpic comes with a built-in [Playground](/distribution/playground) at the `/try` path of your server URL. It lets you and your users test tools through a conversational AI interface directly in the browser — no client setup required.
### Test and debug your MCP server with the MCP Inspector
While in development mode, you can use the [MCP Inspector](https://github.com/modelcontextprotocol/inspector) to test and debug your MCP server:
1. Launch the [MCP Inspector](https://github.com/modelcontextprotocol/inspector)
```bash theme={null}
npx @modelcontextprotocol/inspector
```
2. Select **Streamable HTTP** transport.
3. Add your MCP server URL (e.g. `https://mcp-server-XXXXXXX.alpic.live`).
4. Click **Connect**.
The status should change to **Connected**, and you should be able to access all of your ressources, prompts and tools to test them via the inspector UI.
The MCP Inspector is particularly useful for debugging authentication issues, testing tool parameters, and verifying
that your server responds correctly to different requests.
### Connect your MCP server to any compatible MCP client
You can also connect your MCP server to any MCP client supporting SSE or Streamable HTTP transport. Below are some configuration examples:
```json mcp.json theme={null}
{
"mcpServers": {
"your-server-name": {
"url": "https://mcp-server-XXXXXXX.alpic.live",
"transport": "http"
}
}
}
```
```json mcp.json (with Auth Bearer Token) theme={null}
{
"mcpServers": {
"your-server-name": {
"url": "https://mcp-server-XXXXXXX.alpic.live",
"transport": "http",
"headers": {
"X-API-KEY": "your-api-key-here"
}
}
}
}
```
Replace `https://mcp-server-XXXXXXX.alpic.live` with your actual MCP server URL.
# Troubleshooting
Source: https://docs.alpic.ai/troubleshooting
Common deployment issues and how to fix them
This page covers the most common issues you may encounter when deploying and running your MCP server on Alpic. Each section describes what you see, why it happens, and how to fix it.
Before diving in: verify your project builds and runs locally first. Most deployment issues stem from differences
between your local environment and Alpic's serverless runtime.
## Build fails during install
**What you see:** Build fails during the install phase with errors related to a missing lock file or `pyproject.toml`.
**Why it happens:** Alpic's default install commands expect a lock file in your repository. Without one, the install step fails.
**How to fix it:**
Run your package manager's install command locally (e.g., `pnpm install`) to generate a lock file, then commit it (`pnpm-lock.yaml`, `yarn.lock`, or `package-lock.json`) to your repository. This is the recommended approach.
Alternatively, override the install command in your `alpic.json`:
```json theme={null}
{
"$schema": "https://assets.alpic.ai/alpic.json",
"installCommand": "npm install"
}
```
Override the install command in your `alpic.json`:
```json theme={null}
{
"$schema": "https://assets.alpic.ai/alpic.json",
"installCommand": "uv pip install -r requirements.txt"
}
```
Or migrate to a `pyproject.toml`-based project with `uv init`.
You can also set the install command from **Settings > Build Settings** in the dashboard.
## Custom HTTP endpoints are not exposed
Alpic only routes MCP protocol traffic and standard OAuth endpoints. Custom REST API routes on your server are not reachable from the outside.
**What you see:** HTTP requests to custom paths like `/api/search` or `/rag-stream` return 404.
**Why it happens:** Alpic acts as an MCP gateway, not a generic HTTP proxy. It only forwards [MCP protocol messages](/build-deploy/endpoints) to your server. The exposed paths are `/mcp` (and `/` as an alias), and `/assets/*`.
**How to fix it:** You have two options:
1. **Convert your endpoint into an MCP tool.** The tool is invoked through the standard MCP protocol and has full access to your server's logic.
```diff theme={null}
- @app.get("/api/search")
- def search(q: str):
- return {"results": do_search(q)}
+ @mcp.tool()
+ def search(q: str) -> list[str]:
+ """Search the knowledge base."""
+ return do_search(q)
```
2. **Call the endpoint logic directly from an existing tool.** If you already have a tool that needs data from your endpoint, import and call the underlying function directly instead of making an HTTP request.
Note: for MCP apps, you can invoke tools directly from your widget, using [`window.openai.callTool(name, args)`](https://developers.openai.com/apps-sdk/reference) for ChatGPT, or the portable [`tools/call` JSON-RPC method](https://modelcontextprotocol.io/extensions/apps/overview) over postMessage for Claude. No custom HTTP endpoint needed.
## Assets return 404
**What you see:** Static files (HTML, images, CSS, JavaScript) referenced from your MCP server or ChatGPT App return 404.
**Why it happens:** Assets are served from a separate CDN path, not from your server runtime. They need to be placed in a specific directory to be picked up at build time.
**How to fix it:** Place your static files in an `assets/` directory at your project root. They become available at:
```
https://.alpic.live/assets/
```
For Node.js projects, assets generated during the build step in `/assets/` are also picked up. If both locations contain files, built assets take priority on conflict.
Reference these files in your MCP tools and resources using the relative path `/assets/`.
Learn more about asset hosting in our [Hosting Assets](/build-deploy/assets) guide.
## Deployment doesn't reflect my latest changes
**What you see:** Pushes to your branch don't trigger a deployment, your latest changes aren't live, or the build uses the wrong `package.json` / `pyproject.toml`.
**Why it happens:** Each environment tracks a specific Git branch. Pushes to other branches are silently ignored, no deployment is triggered. Similarly, if your project lives in a subdirectory (monorepo) and the root directory is misconfigured, the build reads configuration from the wrong location.
**How to fix it:**
1. Check your environment's tracked branch in **Environments > \[your environment]**. Click the edit icon next to the branch name to update it.
2. Check the root directory in **Settings > Build Settings**. For monorepos, set it to the subdirectory containing your MCP server (e.g., `packages/my-mcp-server`).
## New environment crashes on startup
**What you see:** Your new environment deploys but crashes at runtime with missing API keys, database URLs, or other configuration errors.
**Why it happens:** Environment variables are **per-environment**, not shared across a project. When you create a new environment, it starts with an empty set of variables, even if your production environment has them all configured.
**How to fix it:** When creating a new environment, manually add all required environment variables for that environment. You can use different values per environment (e.g., a staging API key vs a production one).
A few things to keep in mind:
* **Runtime changes are instant:** Updating an environment variable takes effect immediately, no redeploy needed.
* **Build-time variables need a redeploy:** If a variable is used during the install or build step (e.g., a private registry token), you need to redeploy after changing it.
* **4 KB total limit:** The combined size of all keys and values is capped at 4 KB per environment.
For more on managing environments, see [Environments](/build-deploy/environments).
## Tool calls fail with a timeout error
**What you see:** Tool calls return an `Internal Server Error` or stop responding after approximately 30 seconds.
**Why it happens:** Each tool invocation has a **30-second timeout**. Any single tool call that exceeds this limit is terminated.
**How to fix it:**
* **Optimize the tool:** Reduce API call latency, use smaller models, cache results, or paginate large responses.
* **Use long-running tasks:** For operations that genuinely need more time (data processing, complex API orchestrations, ML inference), implement your tool using the MCP Tasks API. Long-running tasks run on a separate compute path with a default TTL of up to 6 hours.
The 30-second limit applies to each individual tool invocation, not to the entire MCP session. If you're hitting this
limit on a specific tool, consider breaking it into smaller, sequential tool calls.
## Deployment fails with 404 on `/mcp`
**What you see:** Deployment fails with `Server returned status 404 on /mcp after 3 attempts` in build logs.
**Why it happens:** Alpic expects your MCP server to listen on `/mcp` (Streamable HTTP) or `/sse` (SSE). If your server uses a different path (e.g., `/`), the deployment health check fails.
**How to fix it:**
Ensure your server uses the default MCP SDK paths:
```typescript theme={null}
// Streamable HTTP, listens on /mcp by default
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
```
If you're using Express or another framework, mount the MCP handler on `/mcp`:
```typescript theme={null}
app.post("/mcp", handleMcpRequest);
```
With FastMCP, the default path is correct. If you're overriding it, make sure you keep `/mcp`:
```python theme={null}
mcp = FastMCP("my-server")
mcp.run(transport="streamable-http", port=8000) # Listens on /mcp by default
```
## Server shows as "Public" despite OAuth configuration
Your server has OAuth configured locally, but the Alpic dashboard shows "Public" and clients can't authenticate.
**What you see:** The Authentication section in your project settings shows "Public". Clients like ChatGPT report that OAuth is not enabled for your server.
**Why it happens:** Alpic detects OAuth at **deploy time** by sending an unauthenticated `initialize` request to your server. If your server returns `200` instead of `401`, or returns `401` without the correct `WWW-Authenticate` header, Alpic classifies it as public.
**How to fix it:** Your server must return the following on unauthenticated requests:
1. **HTTP 401** status code
2. A `WWW-Authenticate` header with this format:
```
WWW-Authenticate: Bearer resource_metadata="http://localhost:/.well-known/oauth-protected-resource"
```
Your server must also expose `/.well-known/oauth-protected-resource` locally, returning **valid OAuth Protected Resource Metadata**.
After making changes, **redeploy**, OAuth status is only evaluated during deployment.
For a complete guide on configuring OAuth with Alpic, see [OAuth Setup](/secure/auth/oauth-setup).
## "Server already initialized" error
**What you see:** The first tool call succeeds, but subsequent calls fail with `Invalid Request: Server already initialized`.
**Why it happens:** Your server creates a single `StreamableHTTPServerTransport` at module scope and reuses it across requests. The MCP SDK rejects the second initialization attempt.
**How to fix it:** Create a fresh transport per incoming HTTP request:
```typescript theme={null}
// Wrong, reuses transport across warm Lambda invocations
const transport = new StreamableHTTPServerTransport({ sessionIdGenerator: () => randomUUID() });
// Right, fresh transport per request
app.post("/mcp", (req, res) => {
const transport = new StreamableHTTPServerTransport({ sessionIdGenerator: () => randomUUID() });
const server = getServer();
server.connect(transport);
// handle request...
});
```
Alternatively, use **stdio transport** and let Alpic handle transport management entirely. With stdio, you don't manage any HTTP server or transport, just expose your tools.
## Deployment fails after a successful build
**What you see:** The build completes successfully, but the deployment fails during the post-build health check with a timeout error.
**Why it happens:** After building, Alpic verifies your server starts correctly. If your server takes too long to boot (approximately 10 seconds), the health check times out. Common causes: heavy dependencies (PyTorch, sentence-transformers, Playwright) or loading large models at import time.
**How to fix it:**
1. **Trim dependencies:** Remove non-critical packages. Every megabyte adds to cold start time.
2. **Defer expensive initialization:** Don't load ML models or establish database connections at import time. Do it lazily on the first tool call instead.
3. **Pre-download assets at build time:** If your server needs large files (models, datasets), download them during the install command, not at runtime.
```json theme={null}
{
"$schema": "https://assets.alpic.ai/alpic.json",
"installCommand": "uv sync && uv run python download_models.py"
}
```
4. **Minimize import chains:** In Python, importing `torch` or `transformers` at the top level triggers heavy initialization. Use lazy imports inside tool functions.
***
## Still stuck?
If none of the above resolves your issue:
* Check the build logs in your [Alpic Dashboard](https://app.alpic.ai) under **Deployments** for your environment
* Reach out on [Discord](https://discord.gg/2jc92tZQdn) for community help
* Email [support@alpic.ai](mailto:support@alpic.ai) with your project ID and a description of the issue
# User Feedbacks
Source: https://docs.alpic.ai/user-insights/user-feedbacks
Collect feedback from your users or the LLM directly inside your MCP server.
### Overview
When something doesn't work as expected, neither users nor models have a way to tell you. User Feedback adds
a `send_feedback` tool to your MCP server so both the model and the user can report issues and send you
feedback.
The [`@alpic-ai/insights`](https://www.npmjs.com/package/@alpic-ai/insights) (TypeScript) and
[`alpic-ai-insights`](https://pypi.org/project/alpic-ai-insights/) (Python SDK) packages inject the feedback tool into
your server's tool list. Alpic stores the feedback for you to explore in the dashboard.
### 1. Install the package
`bash pnpm add @alpic-ai/insights ``bash pip install alpic-ai-insights # or: uv add alpic-ai-insights `
### 2. Wire it into your server
Choose the entry point for your language and MCP framework.
Use `feedbackMiddleware`, it returns a Skybridge `McpMiddlewareFn` you register via `mcpMiddleware()`. Add it
before your tool/widget registrations:
```typescript theme={null}
import { feedbackMiddleware } from "@alpic-ai/insights";
import { McpServer } from "skybridge/server";
const server = new McpServer({ name: "my-mcp-server", version: "1.0.0" }, { capabilities: {} })
.mcpMiddleware(feedbackMiddleware())
.registerTool(/* ... */);
```
Use `captureFeedback`, it accepts an `McpServer` and patches the `tools/list` and `tools/call` in place.
```typescript theme={null}
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { captureFeedback } from "@alpic-ai/insights";
const server = new McpServer(
{ name: "my-mcp-server", version: "1.0.0" },
{ capabilities: {} },
);
server.registerTool(/* ... */);
captureFeedback(server);
```
Register your tools first, then enable the feedback tool before starting the server:
```python theme={null}
from alpic_ai_insights import capture_feedback
from mcp.server.fastmcp import FastMCP
mcp = FastMCP("my-mcp-server")
@mcp.tool()
def search(query: str) -> str:
return query
capture_feedback(mcp)
```
`capture_feedback` also accepts `mcp.server.lowlevel.Server`. Register its `list_tools` and `call_tool`
handlers first, then enable capture:
```python theme={null}
from alpic_ai_insights import capture_feedback
from mcp import types
from mcp.server.lowlevel import Server
server = Server("my-mcp-server")
@server.list_tools()
async def list_tools() -> list[types.Tool]:
return []
@server.call_tool()
async def call_tool(name, arguments):
return [types.TextContent(type="text", text="ok")]
capture_feedback(server)
```
The `FastMCP` example above uses the class bundled with the official MCP Python SDK
(`mcp.server.fastmcp.FastMCP`), not the separate `fastmcp` package.
The middleware adds a `send_feedback` tool to your server at runtime. It is handled entirely by the middleware; you don't need to register it yourself.
### 3. How the LLM uses it
The tool instructs the LLM to:
* Use it **only** for feedback about your MCP server, not about other MCP servers.
* Call it **autonomously** when it detects a genuine issue (e.g. a tool that failed unexpectedly, an unhelpful response, a missing capability). No explicit user consent required.
* Strip any **Personally Identifiable Information** from the content before sending.
The tool accepts two arguments:
| Argument | Required | Description |
| --------- | -------- | -------------------------------------------------------------------------------------------------- |
| `content` | Yes | The feedback content, stripped of any PII. |
| `source` | Yes | Who initiated the feedback: `"user"` if the user asked to send it, `"model"` if sent autonomously. |
### 4. Deploy
Deploy on Alpic as usual. Once the new version is live on the [production environment](/build-deploy/environments), feedback starts flowing in.
### 5. View feedback in the dashboard
In the Alpic dashboard, open your project and click the **Insights** tab, then **Feedbacks**. Each entry shows:
* **Content:** what the feedback says
* **Source:** whether it was sent by the user or the model autonomously
* **Date:** when it was submitted
### Optional: run a custom handler alongside Alpic
Pass a `handler` to run your own logic (e.g. forwarding to Slack or your own analytics) on every captured feedback.
The handler runs **in addition to** Alpic's dashboard delivery (feedback still appears in the **Feedbacks** page) and executes inside your MCP server process:
```typescript theme={null}
.mcpMiddleware(
feedbackMiddleware({
handler: async ({ content, source }) => {
await slack.postMessage({ text: `Feedback [${source}]: ${content}` });
},
}),
)
```
```typescript theme={null}
captureFeedback(server, {
handler: async ({ content, source }) => {
await slack.postMessage({ text: `Feedback [${source}]: ${content}` });
},
});
```
```python theme={null}
async def handle_feedback(feedback):
await slack.post_message(
text=f"Feedback [{feedback.source}]: {feedback.content}",
)
capture_feedback(mcp, handler=handle_feedback)
```
### Combining with User Intents
`feedbackMiddleware`/`captureFeedback` and `intentMiddleware`/`captureIntents` are independent and can be used together:
```typescript theme={null}
import { feedbackMiddleware, intentMiddleware } from "@alpic-ai/insights";
const server = new McpServer({ name: "my-mcp-server", version: "1.0.0" }, { capabilities: {} })
.mcpMiddleware(feedbackMiddleware())
.mcpMiddleware(intentMiddleware())
.registerTool(/* ... */);
```
```typescript theme={null}
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { captureFeedback, captureIntents } from "@alpic-ai/insights";
const server = new McpServer(
{ name: "my-mcp-server", version: "1.0.0" },
{ capabilities: {} },
);
server.registerTool(/* ... */);
captureFeedback(server);
captureIntents(server);
```
```python theme={null}
from alpic_ai_insights import capture_feedback, capture_intents
capture_feedback(mcp)
capture_intents(mcp)
```
`intentMiddleware`/`captureIntents` and `capture_intents` automatically skip the `send_feedback` tool so they don't
inject a `user_intent` field into the feedback tool's schema.
# User Intents
Source: https://docs.alpic.ai/user-insights/user-intents
Understand what your users really want from your MCP server.
### Overview
MCP apps and servers can see tool calls and their parameters, but not the conversation that triggered them.
User Intents offer an easy way to gather the user's intent behind each tool call, and help you analyze and categorise them.
The [`@alpic-ai/insights`](https://www.npmjs.com/package/@alpic-ai/insights) (TypeScript) and
[`alpic-ai-insights`](https://pypi.org/project/alpic-ai-insights/) (Python SDK) packages dynamically add an extra
parameter to all your tools, asking the LLM to include the user intent while making sure to remove any
**Personally Identifiable Information**. Alpic then collects and stores those intents for you to explore.
### 1. Install the package
`bash pnpm add @alpic-ai/insights ``bash pip install alpic-ai-insights # or: uv add alpic-ai-insights `
### 2. Wire it into your MCP app/server
Choose the entry point for your language and MCP framework.
Use `intentMiddleware`: it returns a Skybridge `McpMiddlewareFn` you can register via `mcpMiddleware()`. Add it
before your tool/widget registrations:
```typescript theme={null}
import { intentMiddleware } from "@alpic-ai/insights";
import { McpServer } from "skybridge/server";
const server = new McpServer(
{ name: "my-mcp-server", version: "1.0.0" },
{ capabilities: {} },
)
.mcpMiddleware(intentMiddleware())
.registerTool(/* ... */);
```
Use `captureIntents`, it accepts an `McpServer` and patches the `tools/list` and `tools/call` in place.
```typescript theme={null}
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { captureIntents } from "@alpic-ai/insights";
const server = new McpServer(
{ name: "my-mcp-server", version: "1.0.0" },
{ capabilities: {} },
);
server.registerTool(/* ... */);
captureIntents(server);
```
Register your tools first, then call `capture_intents` before starting the server:
```python theme={null}
from alpic_ai_insights import capture_intents
from mcp.server.fastmcp import FastMCP
mcp = FastMCP("my-mcp-server")
@mcp.tool()
def search(query: str) -> str:
return query
capture_intents(mcp)
```
`capture_intents` also accepts `mcp.server.lowlevel.Server`. Register both the `list_tools` and `call_tool`
handlers before enabling capture:
```python theme={null}
from typing import Any
from alpic_ai_insights import capture_intents
from mcp import types
from mcp.server.lowlevel import Server
server = Server("my-mcp-server")
@server.list_tools()
async def list_tools() -> list[types.Tool]:
return [
types.Tool(
name="search",
inputSchema={
"type": "object",
"properties": {"query": {"type": "string"}},
"required": ["query"],
},
)
]
@server.call_tool()
async def call_tool(name: str, arguments: dict[str, Any]):
return [types.TextContent(type="text", text=str(arguments["query"]))]
capture_intents(server)
```
The `FastMCP` example above uses the class bundled with the official MCP Python SDK
(`mcp.server.fastmcp.FastMCP`), not the separate `fastmcp` package.
### 3. Deploy
Deploy your application on Alpic as usual. As soon as the new version is live on the [production environment](/build-deploy/environments), intents start flowing in.
### 4. View intents in the dashboard
In the Alpic dashboard, open your project and click the **Insights** tab. You'll see a paginated table with the following columns:
* **Intent:** the user's intent behind the tool call, summarized by the LLM.
* **Tool:** the name of the tool that was called.
* **Client:** the MCP client the call came from (ChatGPT, Claude, Cursor, ...).
* **Category:** automatically categorized into a reusable label. You can modify it directly in the table.
* **Date:** the timestamp of the tool call.
* **Replay:** a link to the matching session replay, shown when session replay is enabled.
Identical intents on the same tool are grouped into a single row with a `×N` badge showing how many times they
occurred. Changing the category on a grouped row applies it to every occurrence in the group.
Use the search box to look through intents, tools and categories, and the **Filters** button to narrow the table by
tool, client, or category. Select rows to categorize or delete several intents at once.
### 5. Spot trends and signals
**Hot Categories** & **Signals** offer you a high-level overview of user intents trends over the selected period.
**Hot categories** show you the most common intent categories. It helps you understand what your users are trying to do with your MCP App/server.
**Signals** give you an overview of the important trends in user intent between the current period and the previous one. Switch the time range to update it.
### Optional: run a custom handler alongside Alpic
Pass a `handler` to run your own logic (e.g. sending intents to your own analytics pipeline) on every captured intent.
The handler runs **in addition to** Alpic's dashboard delivery, intents still appear in the **User Insights** page.
The handler runs inside your MCP server process:
```typescript theme={null}
.mcpMiddleware(
intentMiddleware({
handler: async ({ toolName, userPrompt }) => {
await myAnalytics.track("mcp_tool_call", { toolName, userPrompt });
},
}),
)
```
```typescript theme={null}
captureIntents(server, {
handler: async ({ toolName, userPrompt }) => {
await myAnalytics.track("mcp_tool_call", { toolName, userPrompt });
},
});
```
```python theme={null}
async def handle_intent(prompt):
await analytics.track(
"mcp_tool_call",
{"tool_name": prompt.tool_name, "user_prompt": prompt.user_prompt},
)
capture_intents(mcp, handler=handle_intent)
```
### Optional: capture from specific tools only
By default, `intentMiddleware` injects the `user_intent` field into every tool's schema. Use the `tools` option to
restrict capture to a subset of your tools:
```typescript theme={null}
.mcpMiddleware(
intentMiddleware({
tools: ["search", "ask"],
}),
)
```
```typescript theme={null}
captureIntents(server, {
tools: ["search", "ask"],
});
```
```python theme={null}
capture_intents(mcp, tools=["search", "ask"])
```
### Optional: capture from an existing tool field
If your tool already has a parameter that conveys user intent (for example, a `query`, `rationale`, or `question`
parameter on a search tool), you can capture its value instead of asking the LLM to copy the intent into a synthetic
`user_intent` field.
Use the `argumentNameOverride` option in TypeScript or `argument_name_override` in Python: a map of tool names to
the input field whose value should be captured as the intent.
```typescript theme={null}
.mcpMiddleware(
intentMiddleware({
argumentNameOverride: {
search: "query", // The tool "search" has a "query" parameter that conveys user intent
ask: "question", // The tool "ask" has a "question" parameter that conveys user intent
},
}),
)
```
```typescript theme={null}
captureIntents(server, {
argumentNameOverride: {
search: "query", // The tool "search" has a "query" parameter that conveys user intent
ask: "question", // The tool "ask" has a "question" parameter that conveys user intent
},
});
```
```python theme={null}
capture_intents(
mcp,
argument_name_override={
"search": "query",
"ask": "question",
},
)
```
### 6. Export your data
Export your intent data as a CSV file whenever you want to analyze it in your own tools or combine it with your existing workflows.