Skip to content

Commit 332b711

Browse files
os-zhuangclaude
andauthored
feat(mcp): plugin-carried Connect-an-agent Setup page + nav (#2714) (#2741)
* feat(mcp): plugin-carried Connect-an-agent Setup page + nav (#2714) CONNECT_AGENT_UI_BUNDLE registered on kernel:ready via the manifest service (same pattern as the marketplace pages), gated on isMcpServerEnabled(). Page body = objectui's mcp:connect-agent widget (objectui#2372). zh-CN nav label. Browser-verified end-to-end with the live widget: nav entry renders under Integrations, page loads, on-page-minted key drives real MCP tools. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * docs: point connect flows at the official plugin and the Connect-an-Agent page --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
1 parent 0a8e685 commit 332b711

8 files changed

Lines changed: 156 additions & 2 deletions

File tree

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
---
2+
'@objectstack/mcp': minor
3+
'@objectstack/platform-objects': patch
4+
---
5+
6+
feat(mcp): plugin-carried "Connect an agent" Setup page (#2714 Phase 1)
7+
8+
The MCP plugin now registers a Setup page (`connect_agent`) plus its
9+
navigation entry under Integrations — the nav lives and dies with the
10+
capability (cloud ADR-0009 principle) and follows the surface's default-on
11+
switch: an opted-out deployment (`OS_MCP_SERVER_ENABLED=false`) gets no page
12+
and no entry. The page body is the `mcp:connect-agent` SDUI widget provided
13+
by objectui (objectui#2372): env MCP URL, per-client connect cards, SKILL.md
14+
download, API-key minting. zh-CN nav label included.

content/docs/ai/agents.mdx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,9 @@ instead — see the callout in the [AI Overview](/docs/ai).)
4646

4747
Every deployment serves MCP at `/api/v1/mcp` by default (a core platform
4848
capability — set `OS_MCP_SERVER_ENABLED=false` to opt out), with two
49-
authentication tracks:
49+
authentication tracks: The in-product entry point is the
50+
**Setup → Connect an Agent** page: per-client connect snippets, the portable
51+
SKILL.md download (`GET /api/v1/mcp/skill`), and API-key minting.
5052

5153
- **OAuth 2.1 (interactive clients — recommended).** Each deployment is its
5254
own spec-compliant authorization server: the endpoint publishes

content/docs/getting-started/build-with-claude-code.mdx

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -319,6 +319,17 @@ claude mcp add --transport http support-desk https://your-deployment.example.com
319319
# first tool use opens a browser login — you're connected as yourself
320320
```
321321

322+
Or install the [official plugin](https://github.com/objectstack-ai/claude-plugin)
323+
it bundles the portable ObjectStack agent skill and a guided `/objectstack:connect`
324+
command (one plugin serves every deployment; the URL is the only input):
325+
326+
```bash
327+
claude plugin marketplace add objectstack-ai/claude-plugin
328+
```
329+
330+
Admins find every client's copy-paste-ready connect snippet — plus the SKILL.md
331+
download and API-key minting — on the **Setup → Connect an Agent** page.
332+
322333
For headless callers (CI, scripts), mint an API key instead
323334
(`POST /api/v1/keys`, shown once) and pass it as a header:
324335

packages/mcp/src/__tests__/plugin.test.ts

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -236,4 +236,37 @@ describe('MCPServerPlugin', () => {
236236
await plugin.destroy();
237237
});
238238
});
239+
240+
describe('connect-agent UI bundle (plugin-carried Setup page)', () => {
241+
async function startWithManifest(env?: string) {
242+
if (env === undefined) delete process.env.OS_MCP_SERVER_ENABLED;
243+
else process.env.OS_MCP_SERVER_ENABLED = env;
244+
const manifest = { register: vi.fn() };
245+
const ctx = createMockPluginContext({ manifest });
246+
const plugin = new MCPServerPlugin();
247+
await plugin.init(ctx as any);
248+
await plugin.start(ctx as any);
249+
// Replay the kernel:ready hooks the plugin registered.
250+
for (const call of (ctx.hook as any).mock.calls) {
251+
if (call[0] === 'kernel:ready') await call[1]();
252+
}
253+
return manifest;
254+
}
255+
256+
it('registers the Connect-an-Agent page by default (surface default-on)', async () => {
257+
const manifest = await startWithManifest();
258+
expect(manifest.register).toHaveBeenCalledTimes(1);
259+
const bundle = manifest.register.mock.calls[0][0];
260+
expect(bundle.id).toBe('com.objectstack.mcp.connect-agent-ui');
261+
expect(bundle.pages[0].name).toBe('connect_agent');
262+
expect(bundle.navigationContributions[0].app).toBe('setup');
263+
expect(bundle.navigationContributions[0].items[0].pageName).toBe('connect_agent');
264+
});
265+
266+
it('registers nothing when the MCP surface is opted out', async () => {
267+
const manifest = await startWithManifest('false');
268+
expect(manifest.register).not.toHaveBeenCalled();
269+
});
270+
});
271+
239272
});

packages/mcp/src/connect-ui.ts

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.
2+
3+
/**
4+
* "Connect an agent" Setup page — plugin-carried UI metadata (#2714 Phase 1,
5+
* objectui#2363).
6+
*
7+
* The page ships WITH the MCP capability (same principle as the marketplace
8+
* pages in `@objectstack/cloud-connection`, cloud ADR-0009: the nav lives and
9+
* dies with the capability — no MCP plugin, no entry). The page body is the
10+
* SDUI widget `mcp:connect-agent`, provided by objectui's console app-shell:
11+
* it reads `/discovery` for the environment's MCP URL, renders per-client
12+
* connect cards (claude.ai / Claude Desktop / Claude Code / Cursor / VS Code /
13+
* Codex), mints API keys for headless callers, and links the SKILL.md
14+
* download (`GET /api/v1/mcp/skill`).
15+
*
16+
* Registered by {@link MCPServerPlugin} on `kernel:ready`, gated on the same
17+
* default-on switch as the HTTP surface — an opted-out deployment
18+
* (`OS_MCP_SERVER_ENABLED=false`) gets no page and no nav entry.
19+
*/
20+
21+
export const CONNECT_AGENT_PAGE = {
22+
name: 'connect_agent',
23+
label: 'Connect an Agent',
24+
type: 'app' as const,
25+
template: 'default',
26+
kind: 'full' as const,
27+
isDefault: false,
28+
regions: [
29+
{
30+
name: 'header',
31+
width: 'full' as const,
32+
components: [
33+
{
34+
type: 'page:header',
35+
properties: {
36+
title: 'Connect an Agent',
37+
subtitle:
38+
'Give any MCP-capable AI client governed access to this environment — ' +
39+
'every call runs under the caller\'s own permissions and row-level security.',
40+
icon: 'bot',
41+
},
42+
},
43+
],
44+
},
45+
{
46+
name: 'main',
47+
width: 'large' as const,
48+
components: [{ type: 'mcp:connect-agent', properties: {} }],
49+
},
50+
],
51+
};
52+
53+
export const CONNECT_AGENT_UI_BUNDLE = {
54+
id: 'com.objectstack.mcp.connect-agent-ui',
55+
namespace: 'sys',
56+
version: '0.1.0',
57+
type: 'plugin',
58+
scope: 'system',
59+
name: 'Connect an Agent UI',
60+
description: 'Setup page + navigation for connecting MCP clients to this environment.',
61+
pages: [CONNECT_AGENT_PAGE],
62+
navigationContributions: [
63+
{
64+
app: 'setup',
65+
group: 'group_integrations',
66+
priority: 110,
67+
items: [
68+
{
69+
id: 'nav_connect_agent',
70+
type: 'page',
71+
pageName: 'connect_agent',
72+
label: 'Connect an Agent',
73+
icon: 'bot',
74+
},
75+
],
76+
},
77+
],
78+
};

packages/mcp/src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,3 +30,4 @@ export {
3030
OBJECTSTACK_SKILL_DESCRIPTION,
3131
} from './skill.js';
3232
export type { RenderSkillOptions } from './skill.js';
33+
export { CONNECT_AGENT_PAGE, CONNECT_AGENT_UI_BUNDLE } from './connect-ui.js';

packages/mcp/src/plugin.ts

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.
22

33
import type { Plugin, PluginContext } from '@objectstack/core';
4-
import { readEnvWithDeprecation } from '@objectstack/types';
4+
import { readEnvWithDeprecation, isMcpServerEnabled } from '@objectstack/types';
55
import type { IAIService, IDataEngine, IMetadataService } from '@objectstack/spec/contracts';
66
import { MCPServerRuntime } from './mcp-server-runtime.js';
77
import type { MCPServerRuntimeConfig } from './mcp-server-runtime.js';
88
import type { ToolRegistry } from './types.js';
9+
import { CONNECT_AGENT_UI_BUNDLE } from './connect-ui.js';
910

1011
/**
1112
* Configuration options for the MCPServerPlugin.
@@ -137,6 +138,19 @@ export class MCPServerPlugin implements Plugin {
137138
);
138139
}
139140

141+
// ── Plugin-carried Setup UI (cloud ADR-0009 principle) ──
142+
// "Connect an agent" page + nav entry ship WITH the MCP capability and
143+
// follow the HTTP surface's default-on switch: an opted-out deployment
144+
// advertises nothing, so it gets no page either.
145+
if (isMcpServerEnabled()) {
146+
ctx.hook('kernel:ready', async () => {
147+
try {
148+
const manifest = ctx.getService<{ register(m: unknown): void }>('manifest');
149+
manifest?.register?.(CONNECT_AGENT_UI_BUNDLE);
150+
} catch { /* no manifest service (bare kernels, tests) */ }
151+
});
152+
}
153+
140154
// Trigger hook for other plugins to extend MCP
141155
await ctx.trigger('mcp:ready', this.runtime);
142156
}

packages/platform-objects/src/apps/translations/zh-CN.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ export const zhCN: TranslationData = {
5858
nav_sharing_rules: { label: '共享规则' },
5959
nav_record_shares: { label: '记录共享' },
6060
nav_api_keys: { label: 'API 密钥' },
61+
nav_connect_agent: { label: '连接智能体' },
6162

6263
nav_approval_processes: { label: '审批流程' },
6364
nav_approval_requests: { label: '审批申请' },

0 commit comments

Comments
 (0)