@adonis-agora/telescope 0.8.3 → 0.8.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +29 -0
- package/dist/src/ai/index.d.ts +1 -1
- package/dist/src/ai/index.js +1 -1
- package/dist/src/alerts/index.d.ts +1 -1
- package/dist/src/alerts/index.js +1 -1
- package/dist/src/index.d.ts +1 -1
- package/dist/src/index.js +1 -1
- package/dist/src/mcp/index.d.ts +1 -1
- package/dist/src/mcp/index.js +1 -1
- package/dist/src/profiling/index.d.ts +1 -1
- package/dist/src/profiling/index.js +1 -1
- package/dist/src/ui/index.d.ts +1 -1
- package/dist/src/ui/index.js +1 -1
- package/dist/src/watchers/index.d.ts +1 -1
- package/dist/src/watchers/index.js +1 -1
- package/dist/stubs/main.d.ts +6 -0
- package/dist/stubs/main.d.ts.map +1 -0
- package/dist/stubs/{main.ts → main.js} +1 -1
- package/dist/stubs/main.js.map +1 -0
- package/package.json +7 -4
- package/skills/telescope-access-mcp/SKILL.md +280 -0
- package/skills/telescope-alerts-ai/SKILL.md +290 -0
- package/skills/telescope-setup/SKILL.md +255 -0
- package/skills/telescope-storage-retention/SKILL.md +292 -0
- package/skills/telescope-watchers/SKILL.md +317 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,34 @@
|
|
|
1
1
|
# @adonis-agora/telescope
|
|
2
2
|
|
|
3
|
+
## 0.8.5
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [#30](https://github.com/DavideCarvalho/adonis-agora-telescope/pull/30) [`ede467a`](https://github.com/DavideCarvalho/adonis-agora-telescope/commit/ede467ac92daa97b829129751f01a41bea759329) Thanks [@DavideCarvalho](https://github.com/DavideCarvalho)! - Add TanStack Intent AI-agent skills. Ship six SKILL.md guides inside both published
|
|
8
|
+
packages (`packages/*/skills/**`, now included in the `files` array): five core
|
|
9
|
+
skills for `@adonis-agora/telescope` (setup, watchers, storage/retention,
|
|
10
|
+
alerts/AI/client-errors, dashboard access control + MCP) and one for
|
|
11
|
+
`@adonis-agora/telescope-ui` (the React console + `/client`). Adds
|
|
12
|
+
`_artifacts/` domain map, skill spec and skill tree at the repo root, a
|
|
13
|
+
`tanstack-intent` keyword and `@tanstack/intent` devDependency to both packages,
|
|
14
|
+
and an `.github/workflows/check-skills.yml` CI validation workflow.
|
|
15
|
+
|
|
16
|
+
## 0.8.4
|
|
17
|
+
|
|
18
|
+
### Patch Changes
|
|
19
|
+
|
|
20
|
+
- Fix the published package missing `dist/stubs/main.js`.
|
|
21
|
+
|
|
22
|
+
`configure.ts` imports `{ stubsRoot } from './stubs/main.js'`, but the build ran
|
|
23
|
+
`copy:stubs` _after_ `tsc` and the script did `rm -rf dist/stubs && cp -R stubs/. dist/stubs/`
|
|
24
|
+
— wiping the freshly compiled `dist/stubs/main.js` and leaving only the raw
|
|
25
|
+
`stubs/main.ts` behind. Every published version so far has therefore failed at
|
|
26
|
+
`node ace configure` with `ERR_MODULE_NOT_FOUND` for `./stubs/main.js`.
|
|
27
|
+
|
|
28
|
+
`copy:stubs` now only copies the stub templates (`config/`, `database/`) into
|
|
29
|
+
`dist/stubs/` instead of deleting and replacing the whole directory, so the
|
|
30
|
+
compiled `main.js` survives and ships in the tarball.
|
|
31
|
+
|
|
3
32
|
## 0.8.3
|
|
4
33
|
|
|
5
34
|
### Patch Changes
|
package/dist/src/ai/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/** Keep in sync with this package's `version` in package.json. */
|
|
2
|
-
export declare const VERSION = "0.8.
|
|
2
|
+
export declare const VERSION = "0.8.5";
|
|
3
3
|
export { TelescopeAiDiagnoser } from './telescope_ai_diagnoser.js';
|
|
4
4
|
export type { AnthropicMessagesClient, DiagnoseOptions, TelescopeAiDiagnoserOptions, } from './telescope_ai_diagnoser.js';
|
|
5
5
|
export { normalizeConfidence, parseDiagnosis } from './diagnoser.js';
|
package/dist/src/ai/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/** Keep in sync with this package's `version` in package.json. */
|
|
2
|
-
export declare const VERSION = "0.8.
|
|
2
|
+
export declare const VERSION = "0.8.5";
|
|
3
3
|
export { consoleChannel, customChannel, slackChannel, webhookChannel, } from './alert_channel.js';
|
|
4
4
|
export type { AlertChannel, ChannelFetch, ConsoleSink } from './alert_channel.js';
|
|
5
5
|
export { formatSlackMessage } from './slack_format.js';
|
package/dist/src/alerts/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/** Keep in sync with this package's `version` in package.json. */
|
|
2
|
-
export const VERSION = '0.8.
|
|
2
|
+
export const VERSION = '0.8.5';
|
|
3
3
|
// — channels —
|
|
4
4
|
export { consoleChannel, customChannel, slackChannel, webhookChannel, } from './alert_channel.js';
|
|
5
5
|
// — slack formatting —
|
package/dist/src/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/** Keep in sync with this package's `version` in package.json. */
|
|
2
|
-
export declare const VERSION = "0.8.
|
|
2
|
+
export declare const VERSION = "0.8.5";
|
|
3
3
|
export { EntryType, isBatchOrigin } from './entry.js';
|
|
4
4
|
export type { BatchOrigin, BuiltinEntryType, Entry, RecordInput } from './entry.js';
|
|
5
5
|
export type { EntryQuery, TelescopeStore } from './store.js';
|
package/dist/src/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/** Keep in sync with this package's `version` in package.json. */
|
|
2
|
-
export const VERSION = '0.8.
|
|
2
|
+
export const VERSION = '0.8.5';
|
|
3
3
|
// — entry model —
|
|
4
4
|
export { EntryType, isBatchOrigin } from './entry.js';
|
|
5
5
|
export { InMemoryTelescopeStore } from './stores/memory.js';
|
package/dist/src/mcp/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/** Keep in sync with this package's `version` in package.json. */
|
|
2
|
-
export declare const VERSION = "0.8.
|
|
2
|
+
export declare const VERSION = "0.8.5";
|
|
3
3
|
export { defineConfig, resolveConfig, MCP_TOOL_NAMES } from './define_config.js';
|
|
4
4
|
export type { McpToolName, ResolvedTelescopeMcpConfig, TelescopeMcpConfig, } from './define_config.js';
|
|
5
5
|
export { MCP_INTERNAL_ERROR, MCP_METHOD_NOT_FOUND, MCP_TOOLS, MCP_UNAUTHORIZED, TelescopeMcpServer, } from './server.js';
|
package/dist/src/mcp/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/** Keep in sync with this package's `version` in package.json. */
|
|
2
|
-
export const VERSION = '0.8.
|
|
2
|
+
export const VERSION = '0.8.5';
|
|
3
3
|
// — config —
|
|
4
4
|
export { defineConfig, resolveConfig, MCP_TOOL_NAMES } from './define_config.js';
|
|
5
5
|
// — server (plain, testable JSON-RPC surface) —
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/** Keep in sync with this package's `version` in package.json. */
|
|
2
|
-
export declare const VERSION = "0.8.
|
|
2
|
+
export declare const VERSION = "0.8.5";
|
|
3
3
|
export { defineConfig, resolveConfig } from './define_config.js';
|
|
4
4
|
export type { ResolvedTelescopeCpuProfilingConfig, TelescopeCpuProfilingConfig, } from './define_config.js';
|
|
5
5
|
export { CPU_PROFILE_ENTRY_TYPE, ProfilerService, } from './profiler_service.js';
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/** Keep in sync with this package's `version` in package.json. */
|
|
2
|
-
export const VERSION = '0.8.
|
|
2
|
+
export const VERSION = '0.8.5';
|
|
3
3
|
export { defineConfig, resolveConfig } from './define_config.js';
|
|
4
4
|
export { CPU_PROFILE_ENTRY_TYPE, ProfilerService, } from './profiler_service.js';
|
|
5
5
|
export { CpuProfiler, defaultSessionFactory } from './cpu-profiler.js';
|
package/dist/src/ui/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/** Keep in sync with this package's `version` in package.json. */
|
|
2
|
-
export declare const VERSION = "0.8.
|
|
2
|
+
export declare const VERSION = "0.8.5";
|
|
3
3
|
export { defineConfig, resolveConfig, normalizePath, defaultAuthorize } from './define_config.js';
|
|
4
4
|
export type { AuthorizeHook, ReplayConfig, ResolvedTelescopeUiConfig, TelescopeUiConfig, UiCredentials, } from './define_config.js';
|
|
5
5
|
export { resolveDashboardAuth, performLogin, readSession, sanitizeReturnTo, decideDashboardAuth, SESSION_COOKIE_NAME, } from './auth.js';
|
package/dist/src/ui/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/** Keep in sync with this package's `version` in package.json. */
|
|
2
|
-
export const VERSION = '0.8.
|
|
2
|
+
export const VERSION = '0.8.5';
|
|
3
3
|
// — config —
|
|
4
4
|
export { defineConfig, resolveConfig, normalizePath, defaultAuthorize } from './define_config.js';
|
|
5
5
|
// — built-in `dashboardAuth` login screen (optional; opt-in via `config/telescope_ui.ts`) —
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/** Keep in sync with this package's `version` in package.json. */
|
|
2
|
-
export declare const VERSION = "0.8.
|
|
2
|
+
export declare const VERSION = "0.8.5";
|
|
3
3
|
export type { EmitterLike, Watcher } from './emitter.js';
|
|
4
4
|
export { resolveStore, safeRecord } from './record.js';
|
|
5
5
|
export { buildQueryEntry, DB_QUERY_EVENT, LucidQueryWatcher } from './lucid_query_watcher.js';
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/** Keep in sync with this package's `version` in package.json. */
|
|
2
|
-
export const VERSION = '0.8.
|
|
2
|
+
export const VERSION = '0.8.5';
|
|
3
3
|
// — runtime recording (advanced) —
|
|
4
4
|
export { resolveStore, safeRecord } from './record.js';
|
|
5
5
|
// — Lucid query watcher (the headline watcher) —
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../../stubs/main.ts"],"names":[],"mappings":"AAEA;;;GAGG;AACH,eAAO,MAAM,SAAS,QAAgD,CAAC"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { fileURLToPath } from 'node:url';
|
|
2
|
-
|
|
3
2
|
/**
|
|
4
3
|
* Absolute path to the published stubs directory, resolved from this compiled
|
|
5
4
|
* module's location (`dist/stubs/main.js`). Passed to `codemods.makeUsingStub`.
|
|
6
5
|
*/
|
|
7
6
|
export const stubsRoot = fileURLToPath(new URL('./', import.meta.url));
|
|
7
|
+
//# sourceMappingURL=main.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"main.js","sourceRoot":"","sources":["../../stubs/main.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAEzC;;;GAGG;AACH,MAAM,CAAC,MAAM,SAAS,GAAG,aAAa,CAAC,IAAI,GAAG,CAAC,IAAI,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@adonis-agora/telescope",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.5",
|
|
4
4
|
"description": "Laravel Telescope-style headless observability for AdonisJS — records HTTP requests and every Agora diagnostics event as queryable entries, plus opt-in subpaths for per-technology watchers, a web dashboard, AI exception diagnosis, and alerting.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -97,7 +97,8 @@
|
|
|
97
97
|
"files": [
|
|
98
98
|
"dist/",
|
|
99
99
|
"README.md",
|
|
100
|
-
"CHANGELOG.md"
|
|
100
|
+
"CHANGELOG.md",
|
|
101
|
+
"skills"
|
|
101
102
|
],
|
|
102
103
|
"//sideEffects": "registry.ts publishes the telescope runtime handle on a Symbol.for() global slot at import time — process-global state a bundler told sideEffects:false could tree-shake. The rest is pure.",
|
|
103
104
|
"sideEffects": [
|
|
@@ -134,6 +135,7 @@
|
|
|
134
135
|
"@adonisjs/core": "7.4.0",
|
|
135
136
|
"@adonisjs/lucid": "22.4.2",
|
|
136
137
|
"@anthropic-ai/sdk": "0.116.0",
|
|
138
|
+
"@tanstack/intent": "^0.3.2",
|
|
137
139
|
"@types/better-sqlite3": "7.6.13",
|
|
138
140
|
"@types/node": "20.19.43",
|
|
139
141
|
"better-sqlite3": "12.11.1",
|
|
@@ -152,11 +154,12 @@
|
|
|
152
154
|
"observability",
|
|
153
155
|
"diagnostics",
|
|
154
156
|
"tracing",
|
|
155
|
-
"agora"
|
|
157
|
+
"agora",
|
|
158
|
+
"tanstack-intent"
|
|
156
159
|
],
|
|
157
160
|
"scripts": {
|
|
158
161
|
"build": "tsc -p tsconfig.json && pnpm run copy:stubs && pnpm run copy:assets",
|
|
159
|
-
"copy:stubs": "
|
|
162
|
+
"copy:stubs": "mkdir -p dist/stubs && cp -R stubs/config dist/stubs/ && cp -R stubs/database dist/stubs/",
|
|
160
163
|
"copy:assets": "mkdir -p dist/src/ui && cp src/ui/dashboard.html dist/src/ui/dashboard.html",
|
|
161
164
|
"test": "vitest run --passWithNoTests",
|
|
162
165
|
"test:watch": "vitest",
|
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: telescope-access-mcp
|
|
3
|
+
description: >-
|
|
4
|
+
Dashboard access control and the MCP endpoint in @adonis-agora/telescope —
|
|
5
|
+
config/telescope_ui.ts authorize(ctx) hook vs credentials { token, basic },
|
|
6
|
+
dashboardAuth { secret, ttl, login } login screen, defaultAuthorize (allow outside
|
|
7
|
+
production, deny in production), 401-vs-403 fail-closed guard semantics,
|
|
8
|
+
redirect-wins behaviour, replay/queueActions/cpuProfiling.armEnabled mutation
|
|
9
|
+
gates, and config/telescope_mcp.ts JSON-RPC tools behind the same guard. Use for
|
|
10
|
+
"lock down the dashboard", "dashboard open in production", "401 on /telescope",
|
|
11
|
+
"login wall", "expose telescope to Claude Code", "replay returns 403".
|
|
12
|
+
license: MIT
|
|
13
|
+
metadata:
|
|
14
|
+
type: core
|
|
15
|
+
library: "@adonis-agora/telescope"
|
|
16
|
+
library_version: "0.8.4"
|
|
17
|
+
framework: adonisjs
|
|
18
|
+
sources:
|
|
19
|
+
- "DavideCarvalho/adonis-telescope:docs/dashboard/auth.mdx"
|
|
20
|
+
- "DavideCarvalho/adonis-telescope:docs/packages/mcp.mdx"
|
|
21
|
+
- "DavideCarvalho/adonis-telescope:docs/packages/ui.mdx"
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
# Dashboard access control & MCP
|
|
25
|
+
|
|
26
|
+
Every route serving recorded data runs an `authorize` guard first — every
|
|
27
|
+
`<path>/api/*` endpoint, the SSE stream, metrics and the SPA routes. Out of the box
|
|
28
|
+
the policy is **allow outside production, deny in production** until a credential or
|
|
29
|
+
hook permits it. The MCP subpath reuses exactly the same guard.
|
|
30
|
+
|
|
31
|
+
## Setup
|
|
32
|
+
|
|
33
|
+
The simplest production gate is a built-in credential; either `token` or `basic`,
|
|
34
|
+
or both:
|
|
35
|
+
|
|
36
|
+
```ts
|
|
37
|
+
// config/telescope_ui.ts
|
|
38
|
+
import env from '#start/env'
|
|
39
|
+
import { defineConfig } from '@adonis-agora/telescope/ui'
|
|
40
|
+
|
|
41
|
+
export default defineConfig({
|
|
42
|
+
enabled: true,
|
|
43
|
+
path: '/telescope',
|
|
44
|
+
credentials: { token: env.get('TELESCOPE_UI_TOKEN') },
|
|
45
|
+
})
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
```sh
|
|
49
|
+
curl -H 'Authorization: Bearer s3cr3t' http://localhost:3333/telescope/api/entries
|
|
50
|
+
open 'http://localhost:3333/telescope?token=s3cr3t' # ?token= is convenient but leaky
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Source: `docs/dashboard/auth.mdx` (Built-in credentials).
|
|
54
|
+
|
|
55
|
+
## Core patterns
|
|
56
|
+
|
|
57
|
+
### Pattern 1 — delegate to your app's auth
|
|
58
|
+
|
|
59
|
+
When you set `authorize`, the built-in `credentials` gate is ignored — your hook IS
|
|
60
|
+
the whole policy. It receives your **real** `HttpContext` through a framework-light
|
|
61
|
+
declared type, so narrow it to reach `auth`/`session` at runtime.
|
|
62
|
+
|
|
63
|
+
```ts
|
|
64
|
+
// config/telescope_ui.ts
|
|
65
|
+
export default defineConfig({
|
|
66
|
+
async authorize(ctx) {
|
|
67
|
+
const { auth } = ctx as unknown as {
|
|
68
|
+
auth: { check(): Promise<boolean>; user?: { role?: string } }
|
|
69
|
+
}
|
|
70
|
+
await auth.check()
|
|
71
|
+
return auth.user?.role === 'admin'
|
|
72
|
+
},
|
|
73
|
+
})
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
Guard outcomes: denied with no credential → `401` (+ `WWW-Authenticate`); denied
|
|
77
|
+
with a rejected credential → `403`; a throwing hook → `403` (fail-closed).
|
|
78
|
+
|
|
79
|
+
Source: `docs/dashboard/auth.mdx` (Delegate to your app's auth, Guard behaviour).
|
|
80
|
+
|
|
81
|
+
### Pattern 2 — a real login wall with dashboardAuth
|
|
82
|
+
|
|
83
|
+
For shared human access: a server-rendered login page plus a signed session cookie.
|
|
84
|
+
It composes WITH `authorize` (both must pass). Missing `secret` or `login` throws at
|
|
85
|
+
boot rather than shipping an un-mintable gate.
|
|
86
|
+
|
|
87
|
+
```ts
|
|
88
|
+
// config/telescope_ui.ts
|
|
89
|
+
import env from '#start/env'
|
|
90
|
+
import User from '#models/user'
|
|
91
|
+
import hash from '@adonisjs/core/services/hash'
|
|
92
|
+
import { defineConfig } from '@adonis-agora/telescope/ui'
|
|
93
|
+
|
|
94
|
+
export default defineConfig({
|
|
95
|
+
dashboardAuth: {
|
|
96
|
+
secret: env.get('TELESCOPE_DASHBOARD_SECRET'), // HMAC-SHA256 key, 32+ bytes — required
|
|
97
|
+
ttl: '8h',
|
|
98
|
+
async login(username, password) {
|
|
99
|
+
const user = await User.findBy('email', username)
|
|
100
|
+
if (!user || !(await hash.verify(user.password, password))) return null
|
|
101
|
+
if (!user.isAdmin) return null
|
|
102
|
+
return { id: String(user.id), name: user.fullName } // session user, or null to deny
|
|
103
|
+
},
|
|
104
|
+
},
|
|
105
|
+
})
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
This mounts unguarded-by-necessity `GET/POST <path>/login` and `GET <path>/logout`
|
|
109
|
+
routes (they mint/clear the session only — no recorded data sits behind them).
|
|
110
|
+
An unauthenticated page navigation gets a `302` to the login; an unauthenticated API
|
|
111
|
+
call gets a plain `401`.
|
|
112
|
+
|
|
113
|
+
Source: `docs/dashboard/auth.mdx` (Built-in login screen).
|
|
114
|
+
|
|
115
|
+
### Pattern 3 — redirect instead of answering JSON
|
|
116
|
+
|
|
117
|
+
If the hook sets a `location` header and then returns `false`, the guard leaves the
|
|
118
|
+
redirect alone instead of overwriting it with a `403` body.
|
|
119
|
+
|
|
120
|
+
```ts
|
|
121
|
+
authorize: (ctx) => {
|
|
122
|
+
const { auth } = ctx as unknown as { auth: { user?: { isAdmin?: boolean } } }
|
|
123
|
+
if (auth.user?.isAdmin === true) return true
|
|
124
|
+
ctx.response.redirect('/login?next=/telescope')
|
|
125
|
+
return false // redirect wins only when the hook returns false
|
|
126
|
+
}
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
Source: `docs/dashboard/auth.mdx` (Redirecting instead of answering JSON).
|
|
130
|
+
|
|
131
|
+
### Pattern 4 — expose the store to coding agents over MCP
|
|
132
|
+
|
|
133
|
+
```ts
|
|
134
|
+
// config/telescope_mcp.ts
|
|
135
|
+
import env from '#start/env'
|
|
136
|
+
import { defineConfig } from '@adonis-agora/telescope/mcp'
|
|
137
|
+
|
|
138
|
+
export default defineConfig({
|
|
139
|
+
enabled: true,
|
|
140
|
+
path: '/telescope/mcp',
|
|
141
|
+
credentials: { token: env.get('TELESCOPE_MCP_TOKEN') }, // same guard as the dashboard
|
|
142
|
+
// tools: ['list_entries', 'get_trace', 'get_health'], // hand agents a narrower surface
|
|
143
|
+
})
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
```sh
|
|
147
|
+
claude mcp add --transport http telescope https://your-app.example.com/telescope/mcp \
|
|
148
|
+
--header 'Authorization: Bearer <TELESCOPE_MCP_TOKEN>'
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
Six read tools ship: `list_entries`, `get_entry`, `get_trace`, `get_waterfall`,
|
|
152
|
+
`get_health`, `diagnose_exception`. Redaction and sampling are never bypassed — an
|
|
153
|
+
agent sees exactly what the dashboard sees.
|
|
154
|
+
|
|
155
|
+
Source: `docs/packages/mcp.mdx`.
|
|
156
|
+
|
|
157
|
+
### Pattern 5 — gate the dangerous mutations
|
|
158
|
+
|
|
159
|
+
Replay, CPU-arm and queue retry/enqueue are each disabled by default and answer
|
|
160
|
+
`403` until their ui-config gate is flipped — even when the underlying feature is
|
|
161
|
+
enabled.
|
|
162
|
+
|
|
163
|
+
```ts
|
|
164
|
+
export default defineConfig({
|
|
165
|
+
replay: { enabled: true }, // POST <path>/api/requests/:id/replay — re-runs a real request!
|
|
166
|
+
cpuProfiling: { armEnabled: true }, // POST <path>/api/profiles/arm
|
|
167
|
+
queueActions: { enabled: true }, // queue console retry/enqueue
|
|
168
|
+
})
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
Source: `docs/packages/ui.mdx` (Configuration, Request replay),
|
|
172
|
+
`docs/packages/cpu-profiling.mdx` (arming callout).
|
|
173
|
+
|
|
174
|
+
## Common mistakes
|
|
175
|
+
|
|
176
|
+
### CRITICAL Relying on an obscure path instead of the guard
|
|
177
|
+
|
|
178
|
+
```ts
|
|
179
|
+
// Wrong — renaming the prefix is defense in depth, not authentication:
|
|
180
|
+
defineConfig({ path: '/__telescope' }) // production: denied anyway without a credential/hook
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
```ts
|
|
184
|
+
// Correct — make a deliberate choice for production:
|
|
185
|
+
defineConfig({
|
|
186
|
+
path: '/__telescope',
|
|
187
|
+
credentials: { token: env.get('TELESCOPE_UI_TOKEN') },
|
|
188
|
+
})
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
Mechanism: the default policy denies production until a credential or `authorize`
|
|
192
|
+
hook permits it — but that deny is the *default*, never a guarantee you can skip
|
|
193
|
+
configuring; security notes call this out explicitly.
|
|
194
|
+
Source: `docs/dashboard/auth.mdx` (default policy warn + Security notes).
|
|
195
|
+
|
|
196
|
+
### MEDIUM Setting credentials AND an authorize hook expecting both to run
|
|
197
|
+
|
|
198
|
+
```ts
|
|
199
|
+
// Wrong — the token gate silently stops mattering once authorize is set.
|
|
200
|
+
defineConfig({
|
|
201
|
+
credentials: { token: env.get('TELESCOPE_UI_TOKEN') },
|
|
202
|
+
authorize: () => true, // now the whole policy; credentials ignored
|
|
203
|
+
})
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
```ts
|
|
207
|
+
// Correct — pick one: credentials for simple gates, authorize for real policies.
|
|
208
|
+
defineConfig({ credentials: { basic: { username: 'admin', password: env.get('TELESCOPE_UI_PASSWORD') } } })
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
Mechanism: the docs state the built-in `credentials` gate is ignored whenever
|
|
212
|
+
`authorize` is set — your hook replaces it entirely (`dashboardAuth`, by contrast,
|
|
213
|
+
composes with `authorize`).
|
|
214
|
+
Source: `docs/dashboard/auth.mdx` (Delegate to your app's auth).
|
|
215
|
+
|
|
216
|
+
### MEDIUM Reaching for ctx.auth without narrowing the context
|
|
217
|
+
|
|
218
|
+
```ts
|
|
219
|
+
// Wrong — does not type-check: the declared parameter is a minimal HTTP slice.
|
|
220
|
+
authorize: (ctx) => ctx.auth.user?.isAdmin === true
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
```ts
|
|
224
|
+
// Correct — narrow to the slice you rely on; the REAL HttpContext passes through.
|
|
225
|
+
authorize: (ctx) => {
|
|
226
|
+
const { auth } = ctx as unknown as { auth: { user?: { isAdmin?: boolean } } }
|
|
227
|
+
return auth.user?.isAdmin === true
|
|
228
|
+
}
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
Mechanism: the guard's declared shape is framework-light (`request.qs()`,
|
|
232
|
+
`request.header()`, `response`) so any server can satisfy it; `auth`/`session`
|
|
233
|
+
exist at runtime only after narrowing.
|
|
234
|
+
Source: `docs/dashboard/auth.mdx` (narrowing info callout).
|
|
235
|
+
|
|
236
|
+
### HIGH Exposing MCP in production without a token credential
|
|
237
|
+
|
|
238
|
+
```ts
|
|
239
|
+
// Wrong — relies on the dev-open default; prod denials surface as opaque JSON-RPC errors.
|
|
240
|
+
defineConfig({ enabled: true, path: '/telescope/mcp' })
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
```ts
|
|
244
|
+
// Correct — a token/basic credential for production.
|
|
245
|
+
defineConfig({
|
|
246
|
+
path: '/telescope/mcp',
|
|
247
|
+
credentials: { token: env.get('TELESCOPE_MCP_TOKEN') },
|
|
248
|
+
})
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
Mechanism: MCP has no transport-level 401 — denial rides inside the JSON-RPC
|
|
252
|
+
envelope as `-32001` (`MCP_UNAUTHORIZED`), which compliant clients just report as a
|
|
253
|
+
failed call.
|
|
254
|
+
Source: `docs/packages/mcp.mdx` (guard info callout).
|
|
255
|
+
|
|
256
|
+
### MEDIUM Configuring dashboardAuth without secret or login
|
|
257
|
+
|
|
258
|
+
```ts
|
|
259
|
+
// Wrong — both keys are required; this throws at boot.
|
|
260
|
+
defineConfig({ dashboardAuth: { secret: env.get('TELESCOPE_DASHBOARD_SECRET') } })
|
|
261
|
+
defineConfig({ dashboardAuth: { login: verifyCreds } })
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
```ts
|
|
265
|
+
// Correct — non-empty secret AND a login hook.
|
|
266
|
+
defineConfig({
|
|
267
|
+
dashboardAuth: {
|
|
268
|
+
secret: env.get('TELESCOPE_DASHBOARD_SECRET'),
|
|
269
|
+
ttl: '8h',
|
|
270
|
+
login: verifyCreds,
|
|
271
|
+
},
|
|
272
|
+
})
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
Mechanism: the cookie can never be minted without a signing key and a validator, so
|
|
276
|
+
the config fails closed at startup rather than shipping an un-loginable wall.
|
|
277
|
+
Source: `docs/dashboard/auth.mdx` (Built-in login screen table + warn callout).
|
|
278
|
+
|
|
279
|
+
See also: `telescope-ui-dashboard/SKILL.md` — the SPA behind these gates;
|
|
280
|
+
`telescope-alerts-ai/SKILL.md` — the diagnose route this guard covers.
|