Skip to content

MCP Server

Glasstrace exposes eight debugging tools to AI coding agents via the Model Context Protocol (MCP). Your agent connects once, then queries traces, errors, candidate matches, side-effect evidence, and root cause analysis directly from your development session.

When your agent connects, Glasstrace describes itself as a server-side debugging trace source for Next.js applications. It explains that inbound requests are traced from route handler through database calls, with bounded error evidence and timing.

Use Glasstrace when an HTTP error, failed API call, unexpected server behavior, or database error cannot be diagnosed from client-side symptoms alone.

Common workflows: When investigating a fresh failure, get_latest_error is the typical entry point — it returns the most recent server-side failure with the full causal chain. To see multiple recent errors with status codes, use get_error_list. To search by route fragment, tRPC procedure, method, status, or recent activity window, use find_trace_candidates; it returns candidate traces with exact follow-up arguments. For deeper analysis, get_root_cause returns AI-powered root cause analysis and a suggested fix for a specific traceId when a completed analysis is available for that trace (Anonymous mode, partially-captured traces, and never-analyzed traces get the raw trace data instead; a failed analysis returns an error diagnostic) — call it directly when you already have a traceId from prior context (a previous tool result, telemetry, or a user-provided ID), or after find_trace_candidates returns a relevant candidate when you need to discover one. If get_root_cause returns status: "pending", wait a few seconds and retry. After fixing a bug, get_test_suggestions returns evidence-backed regression-test suggestions (names, descriptions, target paths, and missing assertions — it does not write or run tests). get_trace searches for specific requests by URL, method, status code, and explicit time window. get_session_timeline shows a development session’s traces in order — the current session by default, or an explicitly selected one when you pass sessionId (bounded — defaults to 200 traces, and sets hasMore when older entries were truncated).

Side-effect evidence, when present, is compact and allowlisted to a small set of non-sensitive semantic fields; sensitive data such as raw payloads, recipients, names, bodies, tokens, and links is intentionally omitted, and missing evidence is never proof of absence. See Side-effect evidence summaries below for the full field reference.

Glasstrace is observational only. It does not modify code, run tests, restart servers, or interact with the browser. It tells you what happened on the server — you decide what to do about it.

Glasstrace is not for production use. The SDK checks two environment markers — NODE_ENV=production and VERCEL_ENV=production — and disables itself when either is set, unless you explicitly override the guard with GLASSTRACE_FORCE_ENABLE=true or the forceEnable: true SDK option. A production environment that sets neither marker is not detected by this guard, so do not rely on it as the only thing keeping Glasstrace out of production.

SettingValue
Endpointhttps://api.glasstrace.dev/mcp
ProtocolStreamable HTTP
AuthAuthorization: Bearer <your-api-key>

The bearer token is either your anonymous key (from .glasstrace/anon_key, generated automatically by the SDK) or your developer key (GLASSTRACE_API_KEY environment variable).

Test connectivity with curl:

Terminal window
curl -X POST https://api.glasstrace.dev/mcp \
-H "Authorization: Bearer <your-api-key>" \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-d '{"jsonrpc":"2.0","method":"initialize","params":{"protocolVersion":"2025-03-26","capabilities":{},"clientInfo":{"name":"test","version":"1.0.0"}},"id":1}'

A successful response returns a JSON-RPC result with the server’s capabilities and description; fetch the tool list with a follow-up tools/list request. The Accept header must list both application/json and text/event-stream — the Streamable HTTP transport rejects the request with 406 Not Acceptable otherwise.

Glasstrace registers eight tools with the MCP server. Each tool returns a ToolResponseWrapper envelope:

// Success
{ success: true, data: { /* tool-specific response */ } }
// Failure
{ success: false, diagnostic: ToolDiagnostic }

The full ToolDiagnostic shape — including its recovery fields (recoveryActions, retryParams, recommendedNextStep, closeMatches, recentRoutesSample, windowActivity, humanReadable, and the degraded envelope) — is documented under Response types.

Returns the most recent error trace in your session, including the full span tree. This is the typical entry point when a fresh failure just happened.

None. This tool operates on the current session automatically.

interface GetLatestErrorResponse {
summary: TraceSummary;
spans: SpanSummary[];
sideEffectSummary?: SideEffectSummary;
performanceQuerySummary?: PerformanceQuerySummary;
dataShapeSummary?: DataShapeSummary;
raceConcurrencySummary?: RaceConcurrencySummary;
contextBranchSummary?: ContextBranchSummary;
hints?: Hint[];
}

The four optional category summaries (performanceQuerySummary, dataShapeSummary, raceConcurrencySummary, contextBranchSummary) are compact bug-class projections that appear only when their trace-data extractors trigger; absent fields are normal. hints carries optional out-of-band nudges and is omitted entirely when no hints apply:

interface Hint {
code: "server_action_unlabeled";
message: string;
traceIds: string[];
}

Available on all tiers (Anonymous, Free Trial, Pro).

Request:

{}

Response:

{
"success": true,
"data": {
"summary": {
"traceId": "a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d",
"route": "POST /api/polls/create",
"statusCode": 500,
"error": "PrismaClientKnownRequestError: Unique constraint failed on the fields: (`slug`)",
"errorCategory": "foreign-key-violation",
"affectedComponent": "prisma",
"duration": 142,
"timestamp": 1712534400000,
"enrichmentStatus": "completed"
},
"spans": [
{
"name": "POST /api/polls/create",
"layer": "server",
"duration": 142,
"status": "ERROR",
"error": "PrismaClientKnownRequestError",
"children": [
{
"name": "prisma:client:operation polls.create",
"layer": "database",
"duration": 38,
"status": "ERROR",
"error": "Unique constraint failed on the fields: (`slug`)"
}
]
}
]
}
}
{
"success": false,
"diagnostic": {
"reason": "no_traces_found",
"message": "No error traces found in the implicit current session for the authenticated credential scope. This is not proof no error occurred; the current session may be empty, stale, or unrelated to the failing route."
}
}

Lists recent error traces with filtering and pagination. Use this to see the scope of failures in a session before choosing one trace to inspect with get_trace or get_root_cause.

interface GetErrorListParams {
statusCode?: number;
route?: string;
timeWindow?: TimeWindow;
sessionId?: string;
includeFrameworkInternal?: boolean;
cursor?: string;
limit?: number;
}
interface GetErrorListResponse {
items: ErrorListItem[];
totalCount: number;
cursor?: string;
hasMore: boolean;
hints?: Hint[];
}

Available on all tiers (Anonymous, Free Trial, Pro).

Request:

{
"statusCode": 404,
"sessionId": "abcdef1234567890",
"limit": 10
}

Response:

{
"success": true,
"data": {
"items": [
{
"traceId": "6038964b-a843-402e-be8b-42d4ed81f0f6",
"route": "/_not-found",
"method": "GET",
"statusCode": 404,
"error": "Not Found",
"duration": 31,
"timestamp": 1712534400000,
"requestIdentity": {
"displayRoute": "GET /api/health",
"originalPath": "/api/health",
"sanitizedPath": "/api/health",
"frameworkFallbackRoute": "/_not-found",
"method": "GET",
"operationKind": "route_handler"
},
"summaryProvenance": {
"route": {
"source": "root_request",
"confidence": "high",
"evidencePath": "route"
},
"method": {
"source": "root_request",
"confidence": "high",
"evidencePath": "method"
},
"statusCode": {
"source": "root_request",
"confidence": "high",
"evidencePath": "statusCode"
},
"duration": {
"source": "root_request",
"confidence": "high",
"evidencePath": "duration"
},
"error": {
"source": "root_request",
"confidence": "high",
"evidencePath": "error"
}
}
}
],
"totalCount": 1,
"hasMore": false
}
}

Finds candidate traces before exact lookup. Use this when your agent knows a route fragment, concrete URL, tRPC procedure, HTTP method, status code, session, or recent activity window but does not yet know the exact trace ID or safe get_trace arguments.

Candidate discovery is not root-cause evidence. It returns tenant-scoped candidates, confidence labels, the effective time window searched, and exact follow-up arguments for get_trace, get_root_cause, and sometimes get_session_timeline. Agents should use a suggested exact lookup before citing trace evidence as the cause of a bug.

If timeWindow is omitted, the tool searches a bounded recent window and returns effectiveTimeWindow.defaulted: true with serverNow, start, and end. No candidates means no candidate matched that credential-scoped search; it is not proof that a route was never exercised or that a bug is absent.

NameTypeDefaultDescription
routeLikestring—Prompt locator, route fragment, or tRPC procedure such as polls.comments.list, comments, or inviteMember.
urlstring—Concrete path or transport route such as /api/trpc, /api/trpc/polls.comments.list, or /api/private/polls/123/results.
procedurestring—Exact tRPC procedure name when known. Prefer this over a vague routeLike fragment.
methodstring—HTTP method such as GET or POST. Add when the bug report names a method.
statusCodenumber—HTTP status filter. Useful for error-oriented discovery; a 200 candidate is not proof of success.
timeWindow{ start: number, end: number }last 60 minutesOptional for discovery. If omitted, the response exposes the defaulted effective window.
sessionIdstring—Exact same-credential session ID when known. Returned session IDs are evidence-selection metadata, not user identity.
includeFrameworkInternalbooleanfalseInclude framework/runtime routes such as Next.js fallback routes when debugging framework behavior.
cursorstring—Opaque pagination cursor from a previous candidate response.
limitnumber3Number of candidates per page (max 20).

At least one locator is required: routeLike, url, procedure, method, statusCode, timeWindow, or sessionId.

interface FindTraceCandidatesResponse {
query: {
routeLike?: string;
url?: string;
procedure?: string;
method?: string;
statusCode?: number;
timeWindow?: { start: number; end: number };
sessionId?: string;
includeFrameworkInternal?: boolean;
limit?: number;
cursor?: string;
};
effectiveTimeWindow: EffectiveTimeWindow;
candidates: Array<{
traceId: string;
confidence: "exact" | "high" | "medium" | "low";
diagnosticValue?: "decisive" | "supporting" | "route_only" | "auth_short_circuit" | "weak";
matchReasons: string[];
route: string;
method: string;
statusCode: number;
timestamp: number;
duration: number;
sessionId?: string | null;
requestIdentity?: RequestIdentity;
summaryProvenance?: SummaryProvenance;
sideEffectEvidence?: SideEffectEvidencePresence;
hasSideEffectSummary?: boolean;
sideEffectPreview?: {
operationKinds?: Array<"email" | "calendar_link" | "webhook" | "external_api" | "queue" | "after_callback">;
operations?: string[]; // sanitized operation labels
semanticFieldKeys?: string[]; // allowlisted semantic field keys
statuses?: Array<"scheduled" | "started" | "succeeded" | "failed" | "unknown">;
phases?: Array<"request" | "post_response" | "background" | "unknown">;
};
sourceInspectionHint?: string;
performanceQuerySummary?: PerformanceQuerySummary;
dataShapeSummary?: DataShapeSummary;
raceConcurrencySummary?: RaceConcurrencySummary;
contextBranchSummary?: ContextBranchSummary;
suggestedFollowups: {
getTrace: {
url: string;
method: string;
statusCode?: number;
timeWindow: { start: number; end: number };
limit: number;
};
getRootCause: { traceId: string };
getSessionTimeline?: { sessionId: string };
};
}>;
ambiguity?: {
ambiguous: boolean;
message: string;
};
diagnostic?: CandidateDiagnostic;
locatorAliases?: Array<{
inputKind: "url" | "procedure" | "routeLike";
normalizedInput?: string;
tried?: string[];
suggested?: string[];
sources: Array<"recent_trace_vocabulary" | "trpc_url_normalization">;
applied?: boolean;
}>;
cursor?: string;
hasMore: boolean;
nextRequests?: Array<{
intent: "continue" | "replace_locator" | "widen_window" | "drop_locator" | "include_framework_internal";
tool: "find_trace_candidates";
args: {
routeLike?: string;
url?: string;
procedure?: string;
method?: string;
statusCode?: number;
sessionId?: string;
includeFrameworkInternal?: boolean;
limit: number; // explicit effective page limit
timeWindow: { start: number; end: number }; // explicit search bounds
cursor?: string; // required only for intent: "continue"
};
}>;
// Response-level rollup of the candidates' diagnostic value, plus a
// structured continue-vs-stop signal.
diagnosticValue?: "decisive" | "supporting" | "route_only" | "auth_short_circuit" | "weak" | "none";
recommendedNextStep?: RecommendedNextStep;
maxUsefulFollowups?: number;
}
interface EffectiveTimeWindow {
start: number;
end: number;
serverNow: number;
defaulted: boolean;
retentionBounded?: boolean;
// When true, the tool auto-widened the defaulted window to fit tenant
// traffic and re-queried once; reuse the candidate's
// suggestedFollowups.getTrace.timeWindow for follow-up calls rather
// than re-deriving a window from your original input.
widenedToFitTrafficSignal?: boolean;
}
interface CandidateDiagnostic {
reason: "no_candidates_in_window" | "ambiguous_locator" | "empty_session" | "filters_too_restrictive" | "credential_has_no_traces" | "possible_instrumentation_gap" | "orphaned_partial_evidence";
message: string;
searchedScope: {
credentialScoped: true;
sessionId?: string;
effectiveTimeWindow: EffectiveTimeWindow;
};
recoveryActions: Array<{ label: string; tool?: McpToolName; suggestedParams?: Record<string, unknown> }>;
notAbsenceProof: true;
// Shared no-match recovery envelope — same shapes as ToolDiagnostic;
// see CloseMatch, WindowActivity, and DegradedEnvelope under
// [Response types](#tooldiagnostic) below.
closeMatches?: CloseMatch[]; // max 5, ordered by similarity desc
recentRoutesSample?: string[]; // max 10 (anonymous tenants cap at 3)
nearbyRoutes?: string[]; // max 10; populated only when closeMatches is empty
windowActivity?: WindowActivity;
humanReadable?: string;
envelope?: DegradedEnvelope;
}

RecommendedNextStep and McpToolName are the shared enums documented with ToolDiagnostic under Response types below.

When present, nextRequests supplies complete calls for already-selected discovery actions. continue requires a cursor; changed searches forbid one. Locator replacement supplies exactly one locator, and framework inclusion sets includeFrameworkInternal: true. Bounds must be nonnegative integer milliseconds with start < end. The producer preserves the searched scope except for the change named by intent. These calls do not choose the action, change follow-up budgets or grant permission. Servers may omit the field; the existing recovery fields remain available with their original meanings.

Choose the next action using the existing evidence, stop and follow-up rules. For a discovery action with one valid matching entry, copy its tool and args exactly. Each request pins the final effective window and limit, including after a server-defaulted search or automatic widening. Pagination retains every filter and uses the returned cursor; locator replacement retains the searched window and other filters while starting without a cursor. A widening request changes only the window and is omitted when retention prevents broader coverage. Ambiguous locator removal, source-inspection advice and missing context remain advisory. Complete arguments do not guarantee that another query finds a match.

Candidate responses may include sideEffectEvidence, a compact presence summary. A missing sideEffectEvidence field, or a status such as missing, withheld, or unsupported, is not proof that no side effect happened. Use the candidate’s suggestedFollowups.getTrace arguments to inspect the exact trace before drawing conclusions.

Available on all tiers (Anonymous, Free Trial, Pro).

Request:

{
"routeLike": "polls.comments.list"
}

Response:

{
"success": true,
"data": {
"query": {
"routeLike": "polls.comments.list",
"timeWindow": {
"start": 1712530800000,
"end": 1712534400000
}
},
"effectiveTimeWindow": {
"start": 1712530800000,
"end": 1712534400000,
"serverNow": 1712534400000,
"defaulted": true
},
"candidates": [
{
"traceId": "6038964b-a843-402e-be8b-42d4ed81f0f6",
"confidence": "exact",
"matchReasons": ["tRPC procedure matched polls.comments.list"],
"route": "/api/trpc",
"method": "POST",
"statusCode": 500,
"timestamp": 1712534350000,
"duration": 81,
"sessionId": "abcdef1234567890",
"suggestedFollowups": {
"getTrace": {
"url": "/api/trpc/polls.comments.list",
"method": "POST",
"statusCode": 500,
"timeWindow": {
"start": 1712530800000,
"end": 1712534400000
},
"limit": 10
},
"getRootCause": {
"traceId": "6038964b-a843-402e-be8b-42d4ed81f0f6"
},
"getSessionTimeline": {
"sessionId": "abcdef1234567890"
}
}
}
],
"hasMore": false
}
}
{
"success": true,
"data": {
"query": {
"routeLike": "inviteMember",
"timeWindow": {
"start": 1712530800000,
"end": 1712534400000
}
},
"effectiveTimeWindow": {
"start": 1712530800000,
"end": 1712534400000,
"serverNow": 1712534400000,
"defaulted": true
},
"candidates": [],
"diagnostic": {
"reason": "no_candidates_in_window",
"message": "No candidate traces matched this discovery query in the effective time window. This is not proof the route was never exercised; it only describes the credential-scoped window searched.",
"searchedScope": {
"credentialScoped": true,
"effectiveTimeWindow": {
"start": 1712530800000,
"end": 1712534400000,
"serverNow": 1712534400000,
"defaulted": true
}
},
"recoveryActions": [
{
"label": "Retry with a concrete URL, tRPC procedure, or a wider explicit timeWindow.",
"suggestedParams": {
"timeWindow": {
"start": 1712527200000,
"end": 1712534400000
}
}
}
],
"notAbsenceProof": true
},
"hasMore": false
}
}

Some traces contain compact, allowlisted evidence about outbound side effects such as notifications, calendar links, webhooks, queues, external API calls, or post-response callbacks. The evidence is designed to help an agent understand which safe semantic facts were observed without turning Glasstrace into payload capture or structured log capture.

Side-effect evidence appears in two shapes:

ShapeWhere it appearsHow to use it
sideEffectEvidenceTraceSummary and find_trace_candidates candidatesPresence metadata: status, operation kinds, safe field keys, operation count, omissions, and notAbsenceProof. Use it to decide whether an exact trace lookup is worth pulling.
sideEffectSummaryget_latest_error, get_trace, and get_root_cause trace-bearing responsesFull compact summary for the selected trace: operations, semantic fields, omissions, limits, and evidence paths. Use this before citing side-effect facts in a fix.

The value model is intentionally narrow. Each operation can include operation metadata (kind, operation, status, and phase) plus safe semantic fields. Stable field keys include templateKey, providerOperation, role, locale, timezone, status, and phase. Producers may also emit lower-camel-case keys ending in canonical suffixes: *Class, *Count, *Kind, *Role, or *Holds. *Holds fields are semantic boolean strings ("true" or "false"), not raw booleans.

Omission metadata summarizes why evidence was not retained without echoing the rejected value. Examples include disabled capture, unsupported keys, allowlist-denied semantic fields, oversized values, raw timestamps, unhashed IDs, scalar-cap overflow, and raw payload or secret-shaped data.

Privacy boundaries are part of the contract. Side-effect summaries do not expose recipient email addresses, names, raw message bodies, invite or calendar links, tokens, cookies, auth headers, API keys, raw request or response bodies, raw URLs with query strings, arbitrary structured application logs, or outbound payloads.

Workflow:

  1. Use find_trace_candidates when the agent has a route fragment, concrete URL, tRPC procedure, method, status code, session, or time window.
  2. Treat a candidate with sparse evidence as a prompt to pull the exact trace, not as proof that evidence is absent.
  3. Use get_trace, get_latest_error, or get_root_cause to read the full sideEffectSummary when it is present.
  4. If a *Holds field is "false" and the scalar details are needed, pass the trace’s traceId and a spanId from get_trace or another span-bearing response to get_span_attributes.

Example:

{
"sideEffectSummary": {
"status": "present",
"operations": [
{
"kind": "queue",
"operation": "comment_notification.queue",
"status": "scheduled",
"phase": "post_response",
"semanticFields": [
{
"key": "role",
"value": "account",
"source": "otel_attribute",
"confidence": "high"
},
{
"key": "templateKey",
"value": "NewCommentEmail",
"source": "otel_attribute",
"confidence": "high"
}
],
"omitted": [],
"evidencePath": "spans[2].sideEffect.operations[0]"
}
],
"omitted": [],
"limits": {
"maxOperations": 5,
"maxFieldsPerOperation": 10,
"truncated": false
},
"notAbsenceProof": true
}
}

Search for traces by URL, HTTP method, status code, time window, or correlation ID. Results are paginated (default 20 per page, max 100).

Trace searches must include timeWindow so returned traces are tied to the current debugging context instead of an unrelated older session. URL, method, status code, correlation ID, and cursor filters narrow the search, but they do not replace the time window.

URL searches scan stored and displayed routes in one order, including older traces without request identity metadata. Each call performs bounded work, so items: [] with hasMore: true means the search still has rows to scan. Continue with the returned cursor and the same filters, window and limit. hasMore does not promise another matching trace; an empty page alone does not prove absence.

NameTypeDefaultDescription
urlstring—Filter by request URL (exact match on route)
methodstring—Filter by HTTP method (GET, POST, etc.)
statusCodenumber—Filter by HTTP status code
timeWindow{ start: number, end: number }—Filter by timestamp range (milliseconds since epoch)
correlationIdstring—Filter by browser extension correlation ID
cursorstring—Pagination cursor from a previous response
limitnumber20Number of results per page (max 100)

All parameters are optional.

The schema keeps filters optional for compatibility, but the MCP server returns invalid_input unless timeWindow is present.

interface GetTraceResponse {
items: Array<{
summary: TraceSummary;
spans: SpanSummary[];
sideEffectSummary?: SideEffectSummary;
sourceInspectionHint?: string;
performanceQuerySummary?: PerformanceQuerySummary;
dataShapeSummary?: DataShapeSummary;
raceConcurrencySummary?: RaceConcurrencySummary;
contextBranchSummary?: ContextBranchSummary;
}>;
cursor?: string;
hasMore: boolean;
hints?: Hint[];
}

Available on all tiers (Anonymous, Free Trial, Pro).

Request:

{
"url": "/api/polls",
"method": "POST",
"statusCode": 500,
"timeWindow": {
"start": 1712534100000,
"end": 1712534700000
},
"limit": 5
}

Response:

{
"success": true,
"data": {
"items": [
{
"summary": {
"traceId": "a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d",
"route": "POST /api/polls/create",
"method": "POST",
"statusCode": 500,
"error": "Unique constraint failed on the fields: (`slug`)",
"duration": 142,
"timestamp": 1712534400000,
"requestIdentity": {
"displayRoute": "POST /api/polls/create",
"originalPath": "/api/polls/create",
"sanitizedPath": "/api/polls/create",
"method": "POST",
"operationKind": "route_handler"
},
"summaryProvenance": {
"route": {
"source": "root_request",
"confidence": "high",
"evidencePath": "route"
},
"method": {
"source": "root_request",
"confidence": "high",
"evidencePath": "method"
},
"statusCode": {
"source": "root_request",
"confidence": "high",
"evidencePath": "statusCode"
},
"duration": {
"source": "root_request",
"confidence": "high",
"evidencePath": "duration"
},
"error": {
"source": "root_request",
"confidence": "high",
"evidencePath": "error"
}
}
},
"spans": [
{
"name": "POST /api/polls/create",
"layer": "server",
"duration": 142,
"status": "ERROR",
"children": []
}
]
}
],
"cursor": "eyJ0IjoxNzEyNTM0NDAwMDAwLCJpIjoiYWJjMTIzIn0",
"hasMore": true
}
}
{
"success": false,
"diagnostic": {
"reason": "no_traces_found",
"message": "No traces matched the credential-scoped get_trace filters in the searched time window. This is not proof the route or bug is absent; it only means this exact filter set found no traces.",
"searchedScope": {
"credentialScoped": true,
"credentialScope": "authenticated_credential",
"effectiveTimeWindow": {
"start": 1712534100000,
"end": 1712534700000,
"serverNow": 1712534700000,
"defaulted": false
},
"filters": {
"url": "/api/polls"
}
},
"recoveryActions": [
{
"label": "Use candidate discovery for route fragments, tRPC procedures, or uncertain URLs.",
"tool": "find_trace_candidates",
"suggestedParams": {
"routeLike": "/api/polls",
"timeWindow": {
"start": 1712534100000,
"end": 1712534700000
}
}
}
],
"notAbsenceProof": true
}
}

Returns AI-powered root cause analysis for a specific trace. The enrichment pipeline runs asynchronously after trace ingestion. This tool returns immediately with whatever data is available — it never blocks on an LLM call.

NameTypeDefaultDescription
traceIdstring—Required. The trace ID to analyze.

Successful responses contain one of these status variants in data:

// status: "completed" — enrichment finished successfully
interface GetRootCauseCompleted {
status: "completed";
rootCause: string;
affectedComponent: string;
errorCategory: "foreign-key-violation" | "auth-failure" | "timeout" | "not-found" | "validation-error" | "connection-error" | "unknown";
suggestedFix: string;
confidence: number; // 0 to 1
relatedContext?: string;
codeLocation?: { file: string; line: number; column?: number };
enrichmentContext?: EnrichmentContext;
evidence?: EvidenceCitation[];
missingEvidence?: EvidenceCitation[];
contradictingEvidence?: EvidenceCitation[];
sideEffectSummary?: SideEffectSummary;
sourceInspectionHint?: string;
performanceQuerySummary?: PerformanceQuerySummary;
dataShapeSummary?: DataShapeSummary;
raceConcurrencySummary?: RaceConcurrencySummary;
contextBranchSummary?: ContextBranchSummary;
// "same_span" = the error signal was on the HTTP server span itself;
// "descendant" = it was on a transitive child span (a masked-200
// render-route failure).
boundaryMaskedScope?: "same_span" | "descendant";
hints?: Hint[];
}
// status: "pending" — enrichment is still running
interface GetRootCausePending {
status: "pending";
message: string;
estimatedWaitMs?: number;
enrichmentContext?: EnrichmentContext;
hints?: Hint[];
}
// status: "partial" — trace found, route-level SERVER span never ended
interface GetRootCausePartial {
status: "partial";
partialCause: "route_span_never_ended";
summary: TraceSummary;
spans: SpanSummary[];
message: string;
recoveryActions: Array<{ label: string; tool?: McpToolName; suggestedParams?: Record<string, unknown> }>;
evidence?: EvidenceCitation[];
sideEffectSummary?: SideEffectSummary;
sourceInspectionHint?: string;
hints?: Hint[];
}
// status: "unavailable" — anonymous tier, or any tier when the trace has no enrichment record
interface GetRootCauseUnavailable {
status: "unavailable";
summary: TraceSummary;
spans: SpanSummary[];
message: string;
upgradeUrl?: string; // always an https:// URL
evidence?: EvidenceCitation[];
missingEvidence?: EvidenceCitation[];
sideEffectSummary?: SideEffectSummary;
sourceInspectionHint?: string;
performanceQuerySummary?: PerformanceQuerySummary;
dataShapeSummary?: DataShapeSummary;
raceConcurrencySummary?: RaceConcurrencySummary;
contextBranchSummary?: ContextBranchSummary;
hints?: Hint[];
}

Failed enrichment returns a diagnostic wrapper instead of a data.status variant. Its message distinguishes the recorded timeout, rate limit, parse failure, validation failure, oversized context or missing trace. For example:

{
"success": false,
"diagnostic": {
"reason": "enrichment_failed",
"message": "The enrichment attempt was rate limited by the analysis provider. Calling get_root_cause reads stored state; it does not start another analysis.",
"recoveryActions": [
{
"label": "Inspect available captured span attributes for this trace.",
"tool": "get_span_attributes",
"suggestedParams": { "traceId": "00000000-0000-4000-8000-000000000001" }
}
]
}
}

The action uses the trace ID from the request and reads available captured attributes. It does not retry enrichment or guarantee that particular values exist. If a failed record has no failure reason, the message says that no reason was recorded. Unknown or corrupt stored results retain a separate safe diagnostic message.

enrichmentContext is an optional structured record of the enrichment run’s inputs and provenance; evidence, missingEvidence, and contradictingEvidence are arrays of EvidenceCitation records that back, qualify, or contradict the stated cause.

  • Anonymous: Returns status: "unavailable" with raw trace data (TraceSummary and span tree included). No AI analysis.
  • Free Trial / Pro: Returns the enrichment result (completed or pending) when an enrichment record exists for the trace. A failed analysis — the stored enrichment is failed, or a completed record’s result fails validation — returns an error diagnostic (success: false), not a failed result object. A trace with no enrichment record — never analyzed, for example one captured before enrichment was enabled or linked in after claiming — returns status: "unavailable" with raw trace data, the same shape the Anonymous tier receives.
  • Partial trace: Any tier can receive status: "partial" when the trace exists but the route-level SERVER span never ended.

Request:

{
"traceId": "a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d"
}

Response:

{
"success": true,
"data": {
"status": "completed",
"rootCause": "The slug generation does not check for existing slugs before inserting. When two polls have the same title, the derived slug collides.",
"affectedComponent": "prisma",
"errorCategory": "foreign-key-violation",
"suggestedFix": "Add a uniqueness check or append a random suffix to the slug before inserting.",
"confidence": 0.92,
"codeLocation": {
"file": "src/app/api/polls/create/route.ts",
"line": 24
}
}
}
{
"success": true,
"data": {
"status": "pending",
"message": "Root cause analysis is in progress. Call again in a few seconds."
}
}
{
"success": true,
"data": {
"status": "unavailable",
"summary": {
"traceId": "a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d",
"route": "POST /api/polls/create",
"statusCode": 500,
"error": "Unique constraint failed on the fields: (`slug`)",
"duration": 142,
"timestamp": 1712534400000
},
"spans": [
{
"name": "POST /api/polls/create",
"layer": "server",
"duration": 142,
"status": "ERROR",
"children": []
}
],
"message": "AI root cause analysis requires a trial or pro account, but the raw trace, side-effect evidence, and symptom summary below are still decisive — read them, and use get_trace or get_span_attributes to drill into the spans rather than treating this as a dead end."
}
}

Failed enrichment uses the diagnostic wrapper shown in the response contract above, including the action for reading available captured span attributes.

Returns regression test ideas based on trace data and (optionally) the import graph. The response has three parts:

  • Part 1 — Coverage map (paid tier only): Shows which code paths are tested and untested, derived from the import graph. Requires coverageMapEnabled on your tier (Free Trial or Pro). Enable import graph capture by setting GLASSTRACE_COVERAGE_MAP=true in your project.
  • Part 2 — Regression tests: Specific test cases to catch the error that occurred.
  • Part 3 — Prevention tests: Broader test ideas to prevent similar errors.

Parts 2 and 3 are available on all tiers. Anonymous users receive Parts 2 and 3 only — the coverage map field will contain empty arrays.

NameTypeDefaultDescription
traceIdstring—Required. The trace ID to generate test suggestions for.
interface GetTestSuggestionsResponse {
partialCause?: "route_span_never_ended";
coverage: {
testedPaths: string[];
untestedPaths: string[];
coveragePercent: number; // 0 to 100
};
regressionTests: EvidenceBackedTestSuggestion[];
preventionTests: EvidenceBackedTestSuggestion[];
hints?: Hint[];
}
interface EvidenceBackedTestSuggestion {
name: string;
description: string;
targetPath: string;
evidence?: EvidenceCitation[]; // at least one entry when present
observedTrigger?: EvidenceCitation;
fixtureRequirements?: string[];
missingAssertions?: string[];
}

partialCause: "route_span_never_ended" marks detected missing route-root evidence. The check uses the same partial-trace signal as get_root_cause; returned suggestions remain usable. An omitted marker does not certify a complete trace.

  • Anonymous: Parts 2 and 3 (regression and prevention tests). Coverage map fields return empty arrays and coveragePercent: 0.
  • Free Trial / Pro: All three parts, including coverage map. Requires coverageMapEnabled feature flag (true for Free Trial and Pro).

Request:

{
"traceId": "a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d"
}

Response (paid tier, with coverage map):

{
"success": true,
"data": {
"coverage": {
"testedPaths": ["src/app/api/polls/create/route.ts"],
"untestedPaths": ["src/app/api/polls/[id]/route.ts", "src/lib/slug.ts"],
"coveragePercent": 33
},
"regressionTests": [
{
"name": "duplicate slug collision",
"description": "Create two polls with identical titles and verify the second receives a unique slug.",
"targetPath": "src/app/api/polls/create/route.ts"
}
],
"preventionTests": [
{
"name": "slug uniqueness validation",
"description": "Verify that the slug generation function appends a suffix when a collision is detected.",
"targetPath": "src/lib/slug.ts"
}
]
}
}

Coverage-map unavailability is not a failure: when the coverage map is unavailable (anonymous tier or no import graph), the handler still returns success: true with empty coverage arrays and coveragePercent: 0, and regression and prevention tests are generated from trace data. Other problems — an unknown traceId, for example — return success: false:

{
"success": false,
"diagnostic": {
"reason": "no_traces_found",
"message": "No trace found with ID a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d."
}
}

Returns a chronological timeline of traces in a session — the current session by default, or an explicitly selected one via sessionId. Results are bounded at 200 traces; when older entries were truncated, hasMore is true. The tool accepts no limit or cursor parameter, so a truncated timeline cannot be paginated — narrow the scope instead (for example, filter errors with get_error_list or search with find_trace_candidates). Use this to understand the sequence of requests your application handled.

NameTypeDefaultDescription
sessionIdstring—Filter by session ID. If omitted, uses the current session.
interface GetSessionTimelineResponse {
items: TraceSummary[];
cursor?: string;
hasMore: boolean;
hints?: Hint[];
}

Available on all tiers (Anonymous, Free Trial, Pro).

Request:

{
"sessionId": "abcdef1234567890"
}

Response:

{
"success": true,
"data": {
"items": [
{
"traceId": "11111111-1111-4111-8111-111111111111",
"route": "GET /api/polls",
"method": "GET",
"statusCode": 200,
"duration": 45,
"timestamp": 1712534400000,
"requestIdentity": {
"displayRoute": "GET /api/polls",
"originalPath": "/api/polls",
"sanitizedPath": "/api/polls",
"method": "GET",
"operationKind": "route_handler"
}
},
{
"traceId": "22222222-2222-4222-8222-222222222222",
"route": "POST /api/polls/create",
"method": "POST",
"statusCode": 500,
"error": "Unique constraint failed",
"duration": 142,
"timestamp": 1712534460000,
"requestIdentity": {
"displayRoute": "POST /api/polls/create",
"originalPath": "/api/polls/create",
"sanitizedPath": "/api/polls/create",
"method": "POST",
"operationKind": "route_handler"
},
"summaryProvenance": {
"route": {
"source": "root_request",
"confidence": "high",
"evidencePath": "route"
},
"method": {
"source": "root_request",
"confidence": "high",
"evidencePath": "method"
},
"statusCode": {
"source": "root_request",
"confidence": "high",
"evidencePath": "statusCode"
},
"duration": {
"source": "root_request",
"confidence": "high",
"evidencePath": "duration"
},
"error": {
"source": "root_request",
"confidence": "high",
"evidencePath": "error"
}
}
},
{
"traceId": "33333333-3333-4333-8333-333333333333",
"route": "GET /api/polls",
"method": "GET",
"statusCode": 200,
"duration": 38,
"timestamp": 1712534520000,
"requestIdentity": {
"displayRoute": "GET /api/polls",
"originalPath": "/api/polls",
"sanitizedPath": "/api/polls",
"method": "GET",
"operationKind": "route_handler"
}
}
],
"hasMore": false
}
}
{
"success": false,
"diagnostic": {
"reason": "no_traces_found",
"message": "No traces found for this session in the authenticated credential scope. This is not proof the route or bug is absent; the session may be empty, stale, or not the session that exercised the route."
}
}

Returns allowlisted scalar attributes for a trace span. Use this only when the trace evidence points to a specific span and the compact summary is not enough, for example when a *Holds relation in sideEffectSummary is "false" and the agent needs the scalar values behind that relation.

The result is grouped by span. Scalar values are redacted or normalized at capture time; raw timestamps, raw IDs, credentials, PII, and payloads are not returned. An empty result is not proof that the original behavior did not occur; it means no scalar drill-down was returned for that trace or span.

interface GetSpanAttributesParams {
traceId: string;
spanId?: string;
}

If spanId is omitted, the tool returns scalar-bearing spans for the trace, bounded by the server response limits. Pass a spanId from get_trace, get_latest_error, or another trace-bearing response that includes spans to narrow the result to one span. Completed get_root_cause responses may not include the span tree; use get_trace when you need a spanId for a trace that already has root-cause analysis.

interface GetSpanAttributesResult {
traceId: string;
partialCause?: "route_span_never_ended";
spans: Array<{
spanId: string;
scalars: Array<{
key: string;
value: string | number | boolean;
source: "otel_attribute" | "sdk_normalized" | "product_normalized";
}>;
}>;
}

partialCause: "route_span_never_ended" qualifies the surviving span evidence. The same partial-trace check as get_root_cause runs on the full authorized trace before selecting a span, so an empty selection retains the caveat. An omitted marker does not certify a complete trace.

Available on all tiers (Anonymous, Free Trial, Pro).

Request:

{
"traceId": "a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d",
"spanId": "span-123"
}

Response:

{
"success": true,
"data": {
"traceId": "a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d",
"spans": [
{
"spanId": "span-123",
"scalars": [
{
"key": "deliveryLatencyMs",
"value": 37,
"source": "otel_attribute"
}
]
}
]
}
}

Every tool that returns trace data uses the TraceSummary type:

interface TraceSummary {
traceId: string;
clientAction?: string;
route: string;
method: string;
statusCode: number;
error?: string;
errorEvidence?: ErrorEvidencePacket;
errorCode?: string;
errorCategory?: "foreign-key-violation" | "auth-failure" | "timeout" | "not-found" | "validation-error" | "connection-error" | "unknown";
affectedComponent?: string;
duration: number; // milliseconds
requestIdentity?: RequestIdentity;
summaryProvenance?: SummaryProvenance;
causalLinks?: CausalLink[];
operationBoundaries?: OperationBoundary[];
sideEffectEvidence?: SideEffectEvidencePresence;
timestamp: number; // milliseconds since epoch
correlationConfidence?: "high" | "medium" | "low";
sourceLocation?: { file: string; line: number; mapped: boolean };
enrichmentStatus?: "pending" | "processing" | "completed" | "failed";
// Server Action identity joined from the browser extension's captured
// `Next-Action` header. `name` is optional and currently omitted.
nextAction?: { id: string; name?: string };
// tRPC procedure name when the trace's root server span was a tRPC
// procedure call (e.g., "todos.create").
trpcProcedure?: string;
// Present only when a span on the trace detected a Next.js Server
// Action invocation; absence means "not detected", not "no action".
nextActionDetected?: true;
// "same_span" = the error signal was on the HTTP server span itself;
// "descendant" = it was on a transitive child span (a masked-200
// render-route failure). Absent when the heuristic did not fire.
boundaryMaskedScope?: "same_span" | "descendant";
}

requestIdentity and summaryProvenance are documented in Request identity and summary provenance below. causalLinks and operationBoundaries are optional arrays of compact, allowlisted causal-link and operation-boundary evidence records; absent fields mean no such evidence was retained, not that none occurred.

errorEvidence is present on failing trace summaries when Glasstrace can provide a bounded error packet or explicit missing-evidence state. It can include a schema-safe exception message and a stack summary. When the SDK did not emit stack frames, framework diagnostics, or structured log evidence, the packet marks those facts as missing rather than fabricating a cause.

interface ErrorEvidencePacket {
exceptionMessage?: string;
exceptionType?: string;
stack?: StackSummary;
frameworkErrorSummary?: string;
responseSnippet?: string;
externalLogEvidenceMissing?: boolean;
missingExceptionDetails?: boolean;
capture: {
capturePolicy: {
stackFrames: "disabled" | "mapped_only" | "captured" | "missing" | "unknown";
};
redaction: {
applied: boolean;
rules: string[];
omittedFields: string[];
truncationApplied: boolean;
};
safeForAgent: boolean;
};
redactionNotes?: string[];
}
interface StackSummary {
frames: Array<{
functionName?: string;
file?: string;
line?: number;
column?: number;
sourceKind: "mapped_source" | "compiled_source" | "framework_internal" | "dependency" | "unknown";
mapped: boolean;
inApp: boolean;
redactionNotes?: string[];
}>;
frameCountObserved?: number;
frameCountReturned: number;
framesOmitted: boolean | "unknown";
source: "exception.stacktrace" | "sdk_parsed_frames" | "source_location_attributes" | "framework_error_payload" | "missing";
confidence: "high" | "medium" | "low";
redactionNotes: string[];
}

Span trees represent the request lifecycle:

interface SpanSummary {
// Span identifier for the get_span_attributes drill-down. Optional:
// older span payloads may omit it.
spanId?: string;
name: string;
layer: "server" | "database" | "external";
duration: number; // milliseconds
status: string;
error?: string;
children?: SpanSummary[];
// tRPC procedure identifier for the span (e.g., "todos.create").
trpcProcedure?: string;
// Captured response body for errored HTTP spans; may be truncated to
// an empty string. Gated by the errorResponseBodies capture setting.
errorResponseBody?: string;
// Present only when the SDK detected a Next.js Server Action
// invocation on this span; absence means "not detected".
nextActionDetected?: true;
}

Trace summaries and error-list items may include two optional evidence fields:

  • requestIdentity is a sanitized description of the request or logical operation. It can distinguish a framework route pattern from the original path that the user requested, name a tRPC procedure, identify a tRPC batch, or label middleware and framework fallback routes.
  • summaryProvenance explains where the primary summary fields came from. Each entry records a source such as root_request, child_span_fallback, ingestion_derived, or missing, plus a confidence value and, when available, an evidence path.

Use requestIdentity.displayRoute for the most agent-friendly route label when it is present. Use summaryProvenance when deciding whether a field is strong request evidence or a lower-confidence fallback. Both fields are optional, so older traces and some framework paths may omit them.

For framework fallback traces, requestIdentity.originalPath may recover the user-requested path while summaryProvenance.route still describes the stored summary route value, such as /_not-found or /_error.

interface RequestIdentity {
displayRoute: string;
originalPath?: string;
sanitizedPath?: string;
routePattern?: string;
frameworkFallbackRoute?: string;
transportRoute?: string;
logicalOperation?: string;
method: string;
framework?: "nextjs" | "express" | "hono" | "remix" | "unknown";
operationKind?: "http_route" | "route_handler" | "trpc_procedure" | "server_action" | "middleware" | "framework_internal" | "unknown";
trpc?: {
transportPath: string;
procedureName?: string;
batch?: {
memberIndex?: number;
memberCount: number;
memberProcedures?: string[];
attributionConfidence: "exact" | "inferred" | "unknown";
};
};
}
interface SummaryProvenance {
route: FieldProvenance;
method: FieldProvenance;
statusCode: FieldProvenance;
duration: FieldProvenance;
error: FieldProvenance;
sourceKind?: FieldProvenance;
}
interface FieldProvenance {
source: "root_request" | "root_span" | "child_span_fallback" | "framework_metadata" | "ingestion_derived" | "enrichment_derived" | "missing";
confidence: "high" | "medium" | "low";
evidencePath?: string;
notes?: string;
}

Error and empty-state responses use the ToolDiagnostic type:

interface ToolDiagnostic {
reason: "no_traces_found" | "ttl_expired" | "subscription_required" | "enrichment_pending" | "enrichment_failed" | "database_unreachable" | "tier_insufficient" | "data_not_available" | "invalid_input" | "internal_error";
message: string;
upgradeUrl?: string;
dataPreserved?: boolean;
searchedScope?: {
credentialScoped: true;
credentialScope?: "authenticated_credential";
sessionId?: string;
effectiveTimeWindow?: {
start: number;
end: number;
serverNow?: number;
defaulted?: boolean;
retentionBounded?: boolean;
};
filters?: Record<string, string | number | boolean>;
};
recoveryActions?: Array<{
label: string;
tool?: McpToolName;
suggestedParams?: Record<string, unknown>;
}>;
// Server-derived retry shape on rejection envelopes — copy this object
// straight into the next call to the same tool.
retryParams?: Record<string, unknown>;
notAbsenceProof?: true;
// Structured continue-vs-stop signal.
diagnosticValue?: "decisive" | "supporting" | "route_only" | "auth_short_circuit" | "weak" | "none";
recommendedNextStep?: RecommendedNextStep;
maxUsefulFollowups?: number;
// No-match recovery envelope. Populated on no-match branches (where
// notAbsenceProof: true holds): get_error_list can populate the full
// set including closeMatches; get_latest_error populates
// recentRoutesSample, windowActivity, and humanReadable;
// get_root_cause and get_session_timeline populate humanReadable only.
// get_trace's no-match recovery is served by find_trace_candidates.
closeMatches?: CloseMatch[]; // max 5, ordered by similarity desc
recentRoutesSample?: string[]; // max 10 (anonymous tenants cap at 3)
nearbyRoutes?: string[]; // max 10; populated only when closeMatches is empty — "these routes are in the window", not a similarity match
windowActivity?: WindowActivity;
humanReadable?: string; // server-built prose, max 2048 chars — render as plain text
// Degraded-envelope diagnostic: the recovery envelope itself was
// suppressed by a server-side condition, so an empty envelope is not
// a legitimate "no recovery available" signal.
envelope?: DegradedEnvelope;
}
type McpToolName = "find_trace_candidates" | "get_trace" | "get_error_list" | "get_session_timeline" | "get_latest_error" | "get_root_cause" | "get_test_suggestions" | "get_span_attributes";
type RecommendedNextStep = "get_trace" | "get_root_cause" | "get_latest_error" | "get_error_list" | "get_session_timeline" | "inspect_source" | "retry_with_wider_window" | "retry_with_authenticated_credential" | "retry_with_different_route" | "stop";
interface CloseMatch {
procedure: string;
tracesInWindow: number;
similarity: number; // 0 to 1
suggestedCall: { tool: McpToolName; args: Record<string, unknown> };
}
interface WindowActivity {
totalTracesInWindow: number;
uniqueProceduresInWindow: number;
totalTracesInTenantEver?: number;
captureConfigBlocksRequest?: boolean;
}
interface DegradedEnvelope {
degraded: true;
reason: "scope_unresolvable" | "indexes_not_ready" | "kill_switch" | "prisma_connection_error" | "unknown_error";
notAbsenceProof: true;
humanReadable: string;
}

When any of closeMatches, recentRoutesSample, nearbyRoutes, windowActivity, humanReadable, or envelope is populated, the diagnostic always carries notAbsenceProof: true.

Every MCP tool response is wrapped in this envelope:

// Success
{ success: true, data: { /* tool-specific response */ } }
// Failure
{ success: false, diagnostic: ToolDiagnostic }
FeatureAnonymousFree TrialPro
get_latest_errorAll dataAll dataAll data
find_trace_candidatesAll dataAll dataAll data
get_traceAll dataAll dataAll data
get_error_listAll dataAll dataAll data
get_session_timelineAll dataAll dataAll data
get_span_attributesAll dataAll dataAll data
get_root_cause — raw traceYesYesYes
get_root_cause — AI analysisNoYesYes
get_test_suggestions — regression tests (Part 2)YesYesYes
get_test_suggestions — prevention tests (Part 3)YesYesYes
get_test_suggestions — coverage map (Part 1)NoYesYes
LimitAnonymousFree TrialPro
Traces per minute1005002,000
Storage TTL48 hours7 days90 days
Max trace size500 KB1 MB2 MB
Concurrent sessions1 (advisory)1 (advisory)10

Anonymous mode does not include LLM enrichment. Free Trial and Pro both use the best enrichment model (Anthropic).

Concurrent session limits are advisory, not enforced. If you exceed your tier’s limit, traces from all sessions are still accepted. The dashboard shows a nudge suggesting an upgrade.

Anonymous mode is free, requires no account, and never expires. Sign up for a Free Trial to enable AI-powered root cause analysis and the coverage map.

If you ran npx @glasstrace/sdk init, your agent’s MCP configuration was generated automatically. The instructions below are for manual setup or verification.

Manual fallback for any agent:

Terminal window
npx @glasstrace/sdk mcp add

Config file: .mcp.json (project root)

{
"mcpServers": {
"glasstrace": {
"type": "http",
"url": "https://api.glasstrace.dev/mcp",
"headers": {
"Authorization": "Bearer <your-api-key>"
}
}
}
}

Or register via CLI:

Terminal window
claude mcp add-json glasstrace '{"type":"http","url":"https://api.glasstrace.dev/mcp","headers":{"Authorization":"Bearer <your-api-key>"}}' --scope project

Config file: .codex/config.toml (project) or ~/.codex/config.toml (global)

[mcp_servers.glasstrace]
url = "https://api.glasstrace.dev/mcp"
bearer_token_env_var = "GLASSTRACE_API_KEY"

Set the environment variable in .env.local or export it in your shell:

Terminal window
export GLASSTRACE_API_KEY="<your-api-key>"

Or register via CLI:

Terminal window
codex mcp add glasstrace --url https://api.glasstrace.dev/mcp

Config file: .gemini/settings.json (project) or ~/.gemini/settings.json (global)

{
"mcpServers": {
"glasstrace": {
"httpUrl": "https://api.glasstrace.dev/mcp",
"headers": {
"Authorization": "Bearer <your-api-key>"
}
}
}
}

Or register via CLI:

Terminal window
gemini mcp add --transport http --header "Authorization: Bearer <your-api-key>" glasstrace https://api.glasstrace.dev/mcp

Config file: .cursor/mcp.json (project) or ~/.cursor/mcp.json (global). Requires Cursor v0.48.0+.

{
"mcpServers": {
"glasstrace": {
"type": "http",
"url": "https://api.glasstrace.dev/mcp",
"headers": {
"Authorization": "Bearer <your-api-key>"
}
}
}
}

Cursor does not have a CLI registration command. Create the config file directly.

Config file: ~/.codeium/windsurf/mcp_config.json (global only — no project-scoped config).

{
"mcpServers": {
"glasstrace": {
"serverUrl": "https://api.glasstrace.dev/mcp",
"headers": {
"Authorization": "Bearer <your-api-key>"
}
}
}
}

Windsurf also supports environment variable interpolation:

{
"mcpServers": {
"glasstrace": {
"serverUrl": "https://api.glasstrace.dev/mcp",
"headers": {
"Authorization": "Bearer ${env:GLASSTRACE_API_KEY}"
}
}
}
}

Windsurf does not have a CLI registration command. Create the config file directly.