Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
571ed5a
docs: spec for Internal MCP Server Portals
simplesagar May 20, 2026
b51d14b
docs: implementation plan for Internal MCP Server Portals
simplesagar May 21, 2026
120cead
feat(db): add project_portals table
simplesagar May 21, 2026
6fe5fd6
fix(db): guard tagline against empty string in project_portals
simplesagar May 21, 2026
99daebe
docs(portals): align tagline check with implemented migration
simplesagar May 21, 2026
d616960
feat(portals): add sqlc queries and generated repo
simplesagar May 21, 2026
a4c379b
feat(portals): implement getPortal and updatePortal
simplesagar May 21, 2026
d55edca
feat(portals): resolve portal logo url with project fallback
simplesagar May 21, 2026
3824319
feat(portals): wire portals service into server startup
simplesagar May 21, 2026
2c56ae0
test(portals): assert cross-org getPortal returns 404
simplesagar May 21, 2026
e87d5e0
chore(sdk): regen for portals service
simplesagar May 21, 2026
8553f6d
fix(portals): fall back to project logo when portal has no override
simplesagar May 21, 2026
1f688cc
fix(portals): preserve omitted fields in updatePortal partial updates
simplesagar May 21, 2026
ac3e21d
refactor(portals): inject site URL via constructor
simplesagar May 21, 2026
b7c6c5e
feat(dashboard): add /portal/:slug public-facing portal route
simplesagar May 21, 2026
9fdb3f1
feat(dashboard): portal settings section with live preview
simplesagar May 21, 2026
f65b793
perf(dashboard): lazy-load portal page route
simplesagar May 21, 2026
010d7da
feat(dashboard): add logo upload to portal settings
simplesagar May 21, 2026
cbaa5e8
fix(dashboard): handle clipboard.writeText errors when copying portal…
simplesagar May 21, 2026
7ce7c70
fix(dashboard): reload portal preview iframe after save
simplesagar May 21, 2026
de2db74
feat(dashboard): link portal empty state to catalog
simplesagar May 21, 2026
3559993
fix(portals): allow empty-string clear for logo_asset_id in update
simplesagar May 21, 2026
512850a
test(portals): rewrite cross-org test to invoke project-slug security…
simplesagar May 22, 2026
c9b37ac
chore(portals): drop out-of-order migration ahead of main merge
simplesagar May 22, 2026
3c94bd0
Merge remote-tracking branch 'origin/main' into worktree-portals-spec
simplesagar May 22, 2026
ea60030
fix(portals): wrap uuid.Parse error and add changeset
simplesagar May 23, 2026
6fbf479
chore(portals): regen sqlc-generated files with v1.31.1
simplesagar May 23, 2026
17b403c
chore(portals): drop internal spec + plan docs from PR
simplesagar May 23, 2026
83db5a3
fix(dashboard): unbreak portal settings layout and eager-load portal …
simplesagar May 24, 2026
f6a2a64
fix(dashboard): exempt /portal/ from auth-provider org-slug redirect
simplesagar May 24, 2026
f9be2cd
fix(dashboard): let PortalPage swallow 404s instead of crashing
simplesagar May 24, 2026
2d630a0
refactor(dashboard): move portal to /{orgSlug}/projects/{projectSlug}…
simplesagar May 24, 2026
298bd92
chore: revert accidental mise.lock and pnpm-lock.yaml drift
simplesagar May 24, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions .changeset/internal-mcp-server-portals.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
---
"server": minor
"dashboard": minor
---

Adds **Internal MCP Server Portals** — a per-project, org-internal catalogue page reachable at `app.gram.dev/portal/{project-slug}` that lists every MCP server in the project as cards (server name, description, tool count, link to the existing per-server install page). Project admins toggle the portal on in project settings and brand it with a logo, display name, and tagline.

Surface area:

- New `project_portals` table; one row per project, `enabled = false` by default so no existing project starts serving a portal silently.
- New `portals` Goa service: `getPortal` (org-member read; returns the resolved config plus enriched server cards) and `updatePortal` (project-admin write; read-then-merge partial-update semantics — `nil` preserves, `""` clears, non-empty sets). Disabled portals return 404 to org members; project admins can preview via `?preview=true`.
- Dashboard route `/portal/:projectSlug` (lazy-loaded, auth-gated by `LoginCheck`, 404s uniformly on any failure) plus a new "Internal MCP Portal" section embedded in project settings.
- No new RBAC scopes — reuses `ScopeProjectRead` / `ScopeProjectWrite`.
281 changes: 281 additions & 0 deletions .speakeasy/out.openapi.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 5 additions & 5 deletions .speakeasy/workflow.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

29 changes: 29 additions & 0 deletions client/dashboard/src/pages/portal/PortalCard.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { Type } from "@/components/ui/type";
import { DotCard } from "@/components/ui/dot-card";
import type { PortalServer } from "@gram/client/models/components";

interface Props {
server: PortalServer;
}

export function PortalCard({ server }: Props) {
return (
<a href={server.installUrl} className="block" rel="noopener noreferrer">
<DotCard>
<div className="flex flex-col gap-2">
<Type variant="subheading" as="div">
{server.name}
</Type>
{server.description ? (
<Type small muted className="line-clamp-2">
{server.description}
</Type>
) : null}
<Type small muted>
{server.toolCount} {server.toolCount === 1 ? "tool" : "tools"}
</Type>
</div>
</DotCard>
</a>
);
}
Loading
Loading