Agent-first: REST + MCP coverage of the dashboard surface, both APIs on oRPC#139
Agent-first: REST + MCP coverage of the dashboard surface, both APIs on oRPC#139tifa2UP wants to merge 12 commits into
Conversation
… server Add REST endpoints for organization (get/update/members), API keys (list/create/delete), webhooks CRUD + regenerate-secret, RAG chat with playground parity (normal/agentic/deepResearch, JSON or SSE), and custom domain management. Add a hosted streamable-HTTP MCP server at /api/mcp (mcp-handler) with 37 tools mirroring the v1 surface, authenticated with org API keys. Unify tRPC/REST/MCP onto one service layer, fixing parity divergences: REST namespace create dropped embedding/vector-store configs, re-ingest skipped its webhook event, deleted namespaces served from cache, invalid 204-with-body responses. Security: fix api-key delete IDOR, disable better-auth stock org deletion (bypassed cleanup), honor x-tenant-id in document get/delete/download-urls. Route protectedProcedure through the AgentsetApiError conversion so dashboard mutations surface typed errors instead of 500s.
Add OpenAPI-driven reference pages for the new organization, api-keys, webhooks, chat, hosting-domain, and warm-up endpoints. Rewrite the MCP server page hosted-first with client setup for Claude Code, Claude Desktop, and Cursor plus a complete tool table. Add an agent-first concept page.
|
Too many files changed for review. ( Bypass the limit by tagging |
|
Preview deployment for your docs. Learn more about Mintlify Previews.
💡 Tip: Enable Workflows to automatically generate PRs for you. |
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
…g, handler mounts Public v1 catch-all serves an OpenAPIHandler with the exact legacy success/error envelopes, rate-limit headers, and analytics logging; internal RPC handler replaces the tRPC transport. Organization resource migrated as the exemplar (GET/PATCH/PUT + members) with Speakeasy spec metadata carried on .route(). Legacy organization route files removed (catch-all now serves those paths).
All 21 remaining v1 endpoints are now oRPC procedures behind the catch-all handler (wire-parity: envelopes, status codes, 204s, PUT aliases, pagination, tenant scoping, chat SSE via detailed-output stream passthrough), with the Speakeasy spec metadata carried on .route(). Legacy v1 route files removed. All 52 tRPC procedures ported 1:1 to oRPC internal routers on the same service layer; tRPC files remain until the client migration lands.
All 48 consumer files swap useTRPC()/trpcClient for the module-level orpc utils and imperative client (query keys, partial-match invalidations, optimistic setQueryData writes, isMutating filters, and RouterOutputs types all derived from the oRPC utils). providers.tsx now mounts a plain QueryClientProvider; superjson and the @trpc/* dependencies are gone.
buildOpenApiDocument() (OpenAPIGenerator + ZodToJsonSchemaConverter with compat interceptors + deterministic post-processing) reproduces the legacy zod-openapi document exactly — canonical JSON of old and new specs is byte-identical across all 39 operations and 82 components, so the Speakeasy-generated SDK and Mintlify docs are unaffected. The hand-written spec tree (src/openapi, 58 files) is deleted; shared path/header param schemas move to schemas/api/params.ts and zod-openapi is dropped from the app. Also fixes a handful of new lint errors from the migration waves.
withApiHandler/withNamespaceApiHandler are fully replaced by the oRPC middleware; only HandlerParams (shared by the session/public wrappers still used by the internal chat and hosting routes) and the cached getNamespace lookup (shared with MCP) remain.
Resolves the agentic-search-playground stack (#135/#138/#141) and the free-plan upload limit (#136) against the oRPC migration: - Legacy v1 upload routes and the tRPC billing router were deleted by the migration; main's changes are ported into their oRPC replacements (plan-aware createUpload/createBatchUpload with the file_too_large mapping on REST/RPC/MCP; getTrackedPages removed everywhere). - The playground chat route/schema take main's newer agentic-search implementation; lib/chat (behind the public v1 chat and MCP) keeps the normal/agentic/deepResearch contract, restoring lib/agentic's pipeline entry, lib/deep-research, and the condense prompts that main dropped as dead code, adapted to the NamespaceLanguageModel bundle API from the LLM-getter consolidation. - Generated OpenAPI spec re-verified against the pre-merge snapshot: the only diffs are main's intended ones (gpt-5.5 default, Cohere reranker default, upload-limit descriptions).
4ae9219 to
7650905
Compare
The generic pattern makes the local-only convention self-enforcing: any file or directory named *.local is ignored without needing a dedicated gitignore entry.
Addresses review feedback: the three parallel implementations (internal
oRPC router, public oRPC router, hand-written MCP tools) are now a single
router with a conditional auth middleware.
- server/orpc/base.ts: one auth middleware — if an Authorization header is
present, org API-key auth (wire-parity port: bearer parse, cached key
lookup, per-org rate limit + headers, x-tenant-id, analytics); otherwise
better-auth getSession() + x-organization-id header, membership-checked
and hydrated to the same organization context. requireRoles() gates
session members on mutations the dashboard previously restricted to
admin/owner; API keys keep full org authority (public-API parity).
- server/orpc/router/*: the single router, served by all three surfaces.
Dashboard-only procedures (slug-addressed lookups, richer projections,
billing, onboarding) stay session-gated and carry no route metadata, so
they are excluded from REST routing, the generated spec, and MCP.
- lib/mcp: the 37 hand-written tools are replaced by a generator that walks
the router contract and registers one tool per published operation (39),
with titles/descriptions/read-only/destructive hints from route metadata,
executing via call() through the full middleware chain — REST/MCP parity
by construction.
- lib/orpc.ts + call sites: dashboard mutations moved onto the shared
procedures with a per-call org header (batch-safe: oRPC preserves
per-item headers; RequestHeadersPlugin restores them per batched call).
- lib/api/handler/namespace.ts: fix pre-existing warm-cache 500 on
GET /v1/namespace/{namespaceId} — unstable_cache revives DateTime
columns as strings, failing the z.date() output schema.
The generated OpenAPI document is byte-identical to the previous one
(verified), typecheck/lint/tests are at baseline, and all three surfaces
plus five dashboard flows were smoke-tested end-to-end.
|
Addressed in 4c3e13e — the three implementations are now one oRPC router with the conditional auth check, and MCP is generated from the API contract. One router. if (apiKeyHeader) {
// authenticate via api key (rate limit, tenant, analytics — unchanged wire behavior)
} else {
// authenticate via better-auth session + x-organization-id header (membership + role checks)
}Dashboard mutations now call the same procedures the public API serves (with MCP autogenerated. Net: −1,700 lines, and the generated |
Everything that can be done in the dashboard can now be done via the public REST API and a hosted MCP server, so agents can set up and operate Agentset autonomously — with no breaking changes to existing endpoints. All three surfaces are now served by one oRPC router: the dashboard RPC, the public REST API, and the MCP server run the same procedures, with a single conditional-auth middleware deciding between API-key and session credentials.
Architecture: one router, three surfaces
/api/rpc)httpBatchStreamLinkRPCHandlerover the app router; session resolved once per HTTP request; per-batch-item headers viaRequestHeadersPluginapi.agentset.ai/v1)withApiHandlerwrappersOpenAPIHandlerover the same app router, filtered to route-bearing procedures/openapi.jsonOpenAPIGenerator+ compat post-processing), filtered to operationId-bearing procedures/api/mcp)call()One router, conditional auth
Every procedure runs on the same base middleware (
server/orpc/base.ts):.route()metadata) serve REST, MCP, and the dashboard.requireRoles("admin", "owner")gates session members on the mutations the dashboard previously restricted (namespace create/delete, API keys, webhook mutations); API keys retain full org authority, matching the existing public-API semantics.x-organization-id). oRPC's batch format preserves per-item headers, so batched calls authenticate independently.Wire compatibility is byte-exact. The legacy
{success, data, pagination?}envelopes,{success:false, error:{...}}error bodies (incl. the zod-error 422 format and thedoc_urlanchor quirk), status codes, hiddenPUTaliases, rate-limit headers,x-tenant-idscoping, and cursor pagination are all preserved. Chat withstream: truereturns the exact AI-SDK SSE stream via oRPC's detailed-outputReadableStreampassthrough.The generated OpenAPI document is byte-identical to the previous revision's (which was itself canonically identical to the legacy zod-openapi document) — re-verified after the consolidation, so Speakeasy and Mintlify are unaffected.
New REST endpoints (org API-key auth, OpenAPI-registered)
GET/PATCH /v1/organization,GET /v1/organization/membersGET/POST /v1/api-keys,DELETE /v1/api-keys/{keyId}(headless rotation)GET/POST /v1/webhooks,GET/PATCH/DELETE /v1/webhooks/{webhookId},POST .../regenerate-secretPOST /v1/namespace/{namespaceId}/chat— playground parity (normal/agentic/deepResearch), JSON default,stream: truefor AI-SDK-compatible SSEGET/POST/DELETE /v1/namespace/{namespaceId}/hosting/domainHosted MCP server
https://api.agentset.ai/mcp(streamable HTTP viamcp-handler, SSE disabled) —Authorization: Bearer agentset_…, optionalx-tenant-id. Tools are generated from the router contract (lib/mcp/index.ts, ~200 lines replacing ~1,600 lines of hand-written tools): one tool per published operation — 39 total — with names derived from operationIds (list_namespaces,create_ingest_job, …), titles/descriptions from route metadata, andreadOnlyHint/destructiveHint/idempotentHintannotations from the HTTP method. Tool calls execute via oRPCcall()through the full middleware chain, so auth, rate limiting, tenant scoping, input validation, and error mapping are REST-identical by construction (outputs are the REST envelope;chatpinsstream: false). The stdio@agentset/mcppackage remains the local option.Parity & security fixes shipped alongside
POST /v1/namespacesilently droppedembeddingConfig/vectorStoreConfigand skipped thetotalNamespacescounter; REST re-ingest never emittedingest_job.queued_for_resync; namespace list returnedDELETINGrows;PATCH /v1/namespaceserved stale cache up to 5 min; DELETEs returned invalid 204-with-body.GET /v1/namespace/{namespaceId}:unstable_cacheJSON-serializes the namespace row, so cache hits revivedDateTimecolumns as strings and failed thez.date()output schema.deleteApiKey(deleted by id without org check) — now enforced by the shared procedure's org scoping.POST /api/auth/organization/delete, which hard-deleted orgs bypassing Stripe/namespace cleanup (dashboard uses the service path).x-tenant-idscoping on REST and MCP (previously only list did).AgentsetApiError→ORPCErrormapping at the RPC handler).Docs
17 new OpenAPI-driven reference pages, hosted-first MCP page (Claude Code / Claude Desktop / Cursor setup + tool table), new agent-first concept page.
mintlify broken-linksclean. Pages render once the Speakeasy-hosted spec re-ingests/openapi.json(now generated; canonically identical to the previous document).Verification
search/chat/get_namespacecalls incl. error paths).