Skip to content

Commit fff74b1

Browse files
authored
fix(SMI-2021): use server target for mcp add (#782)
## Summary - bump @smithery/api to 0.66.0 from smithery-ai/typescript-api#103 - send registry-name `smithery mcp add` inputs as Connect `server` targets instead of constructing `server.smithery.ai` URLs locally - keep explicit HTTP URLs on `mcpUrl` and remove the legacy URL normalization helper ## Test plan - pnpm exec tsc --noEmit - pnpm exec biome check - pnpm exec vitest run - pnpm run build (pre-push hook)
1 parent 0b95015 commit fff74b1

12 files changed

Lines changed: 134 additions & 58 deletions

File tree

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ smithery mcp publish <bundle.mcpb> -n <org/server> # Publish an MCP bundle
6969
```bash
7070
# Search and connect to an MCP server
7171
smithery mcp search "github"
72-
smithery mcp add https://server.smithery.ai/github --id github
72+
smithery mcp add github --id github
7373

7474
# Find and call tools from your connected MCP servers
7575
smithery tool find "create issue"

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@
4444
"@anthropic-ai/mcpb": "^1.1.1",
4545
"@biomejs/biome": "2.3.10",
4646
"@modelcontextprotocol/sdk": "^1.25.3",
47-
"@smithery/api": "^0.64.2",
47+
"@smithery/api": "^0.66.0",
4848
"@smithery/sdk": "^4.1.0",
4949
"@types/inquirer": "^8.2.4",
5050
"@types/inquirer-autocomplete-prompt": "^3.0.3",

pnpm-lock.yaml

Lines changed: 5 additions & 5 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/commands/__tests__/mcp-add-impl.test.ts

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,10 @@ vi.mock("../mcp/api", () => ({
3636
ConnectSession: {
3737
create: mockCreateSession,
3838
},
39+
connectionTargetFromInput: (input: string) =>
40+
input.startsWith("http://") || input.startsWith("https://")
41+
? { mcpUrl: input }
42+
: { server: input },
3943
}))
4044

4145
vi.mock("../mcp/output-connection", () => ({
@@ -97,7 +101,10 @@ describe("mcp add duplicate handling", () => {
97101
],
98102
})
99103

100-
await addServer("calclavia/test-input-required-two", {})
104+
await addServer(
105+
"https://server.smithery.ai/calclavia/test-input-required-two",
106+
{},
107+
)
101108

102109
expect(mockCreateConnection).not.toHaveBeenCalled()
103110
expect(mockOutputConnectionDetail).toHaveBeenCalledWith(
@@ -112,6 +119,30 @@ describe("mcp add duplicate handling", () => {
112119
)
113120
})
114121

122+
test("creates registry connections with the server target", async () => {
123+
mockCreateConnection.mockResolvedValue({
124+
connectionId: "github",
125+
name: "github",
126+
mcpUrl: "https://server.smithery.ai/github",
127+
metadata: null,
128+
status: {
129+
state: "connected",
130+
},
131+
})
132+
133+
await addServer("github", {})
134+
135+
expect(mockListConnectionsByUrl).not.toHaveBeenCalled()
136+
expect(mockCreateConnection).toHaveBeenCalledWith(
137+
{ server: "github" },
138+
{
139+
name: undefined,
140+
metadata: undefined,
141+
headers: undefined,
142+
},
143+
)
144+
})
145+
115146
test("prints setupUrl for auth_required duplicate connections", async () => {
116147
mockListConnectionsByUrl.mockResolvedValue({
117148
connections: [

src/commands/__tests__/mcp-add-uplink.test.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,10 @@ vi.mock("../mcp/api", () => ({
4444
ConnectSession: {
4545
create: mockCreateSession,
4646
},
47+
connectionTargetFromInput: (input: string) =>
48+
input.startsWith("http://") || input.startsWith("https://")
49+
? { mcpUrl: input }
50+
: { server: input },
4751
}))
4852

4953
vi.mock("../../lib/uplink", async () => {

src/commands/__tests__/mcp-api.test.ts

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,26 @@ describe("ConnectSession uplink compatibility", () => {
2828
})
2929
})
3030

31+
test("creates registry connections with server targets", async () => {
32+
const create = vi.fn().mockResolvedValue({
33+
connectionId: "exa",
34+
name: "exa",
35+
mcpUrl: "https://server.smithery.ai/exa",
36+
metadata: null,
37+
status: { state: "connected" },
38+
})
39+
40+
const session = new ConnectSession(
41+
{ connections: { create } } as never,
42+
"calclavia",
43+
)
44+
await session.createConnection({ server: "exa" })
45+
46+
expect(create).toHaveBeenCalledWith("calclavia", {
47+
server: "exa",
48+
})
49+
})
50+
3151
test("does not replace conflicting uplink connections on 409", async () => {
3252
const conflict = new ConflictError(409, {}, undefined, new Headers())
3353
const set = vi.fn().mockRejectedValueOnce(conflict)
@@ -80,6 +100,27 @@ describe("ConnectSession uplink compatibility", () => {
80100
})
81101
})
82102

103+
test("sets registry connections with server targets", async () => {
104+
const set = vi.fn().mockResolvedValue({
105+
connectionId: "exa",
106+
name: "exa",
107+
mcpUrl: "https://server.smithery.ai/exa",
108+
metadata: null,
109+
status: { state: "connected" },
110+
})
111+
112+
const session = new ConnectSession(
113+
{ connections: { set } } as never,
114+
"calclavia",
115+
)
116+
await session.setConnection("exa", { server: "exa" })
117+
118+
expect(set).toHaveBeenCalledWith("exa", {
119+
namespace: "calclavia",
120+
server: "exa",
121+
})
122+
})
123+
83124
test("lists tools over smithery.run REST", async () => {
84125
const get = vi.fn().mockResolvedValue({
85126
tools: [

src/commands/mcp/add-impl.ts

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,8 @@ import {
55
completeConnectionAuthorization,
66
finalizeAddedConnection,
77
} from "./add-flow"
8-
import { ConnectSession } from "./api"
8+
import { ConnectSession, connectionTargetFromInput } from "./api"
99
import { isInputRequiredStatus } from "./connection-status"
10-
import { normalizeMcpUrl } from "./normalize-url"
1110
import { outputConnectionDetail } from "./output-connection"
1211
import { parseJsonObject } from "./parse-json"
1312

@@ -29,13 +28,15 @@ export async function addServer(
2928
true,
3029
)
3130

32-
const normalizedUrl = normalizeMcpUrl(mcpUrl)
31+
const target = connectionTargetFromInput(mcpUrl)
3332
const session = await ConnectSession.create(options.namespace)
3433

35-
// Check for existing connections with the same URL
36-
if (!options.force) {
37-
const { connections: existing } =
38-
await session.listConnectionsByUrl(normalizedUrl)
34+
// URL inputs can still be checked exactly. Registry-name inputs are sent
35+
// as `server` so Connect owns canonical URL resolution.
36+
if (!options.force && target.mcpUrl) {
37+
const { connections: existing } = await session.listConnectionsByUrl(
38+
target.mcpUrl,
39+
)
3940
if (existing.length > 0) {
4041
let match = existing[0]
4142
const status = match.status?.state ?? "unknown"
@@ -76,7 +77,7 @@ export async function addServer(
7677
}
7778
}
7879

79-
const connection = await session.createConnection(normalizedUrl, {
80+
const connection = await session.createConnection(target, {
8081
name: options.name,
8182
metadata: parsedMetadata,
8283
headers: parsedHeaders,

src/commands/mcp/add.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,7 @@ import {
1212
addBundleUplinkServer,
1313
type BundleAddTarget,
1414
} from "./add-uplink-bundle"
15-
import { ConnectSession } from "./api"
16-
import { normalizeMcpUrl } from "./normalize-url"
15+
import { ConnectSession, connectionTargetFromInput } from "./api"
1716
import { outputConnectionDetail } from "./output-connection"
1817
import { parseJsonObject } from "./parse-json"
1918
import { classifyAddTarget } from "./uplink-target"
@@ -69,7 +68,7 @@ export async function addServer(
6968
const session = await ConnectSession.create(options.namespace)
7069
const connection = await session.setConnection(
7170
options.id,
72-
normalizeMcpUrl(mcpUrl),
71+
connectionTargetFromInput(mcpUrl),
7372
{
7473
name,
7574
metadata: parsedMetadata,

src/commands/mcp/api.ts

Lines changed: 32 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import type {
55
Connection,
66
ConnectionCreateParams,
77
ConnectionListParams,
8+
ConnectionSetParams,
89
ConnectionsListResponse,
910
} from "@smithery/api/resources/connections.js"
1011
import { createSmitheryClient } from "../../lib/smithery-client"
@@ -14,7 +15,9 @@ import {
1415
} from "../../utils/smithery-settings"
1516

1617
export type { Connection, ConnectionsListResponse }
17-
export type ConnectionTransport = NonNullable<Connection["transport"]>
18+
export type ConnectionTarget =
19+
| (Required<Pick<ConnectionCreateParams, "mcpUrl">> & { server?: never })
20+
| (Required<Pick<ConnectionCreateParams, "server">> & { mcpUrl?: never })
1821

1922
export interface Trigger {
2023
name: string
@@ -156,12 +159,12 @@ export class ConnectSession {
156159
}
157160

158161
async createConnection(
159-
mcpUrl?: string,
162+
target?: string | ConnectionTarget,
160163
options: ConnectionWriteOptions = {},
161164
): Promise<Connection> {
162165
return this.smitheryClient.connections.create(
163166
this.namespace,
164-
buildConnectionBody(mcpUrl, options) as ConnectionCreateParams,
167+
buildConnectionBody(target, options),
165168
)
166169
}
167170

@@ -171,20 +174,20 @@ export class ConnectSession {
171174
*/
172175
async setConnection(
173176
connectionId: string,
174-
mcpUrl?: string,
177+
target?: string | ConnectionTarget,
175178
options: ConnectionWriteOptions = {},
176179
): Promise<Connection> {
177180
try {
178181
return await this.smitheryClient.connections.set(
179182
connectionId,
180-
buildConnectionSetParams(this.namespace, mcpUrl, options),
183+
buildConnectionSetParams(this.namespace, target, options),
181184
)
182185
} catch (error) {
183186
if (error instanceof ConflictError && options.transport !== "uplink") {
184187
await this.deleteConnection(connectionId)
185188
return this.smitheryClient.connections.set(
186189
connectionId,
187-
buildConnectionSetParams(this.namespace, mcpUrl, options),
190+
buildConnectionSetParams(this.namespace, target, options),
188191
)
189192
}
190193
throw error
@@ -255,31 +258,46 @@ export class ConnectSession {
255258
}
256259
}
257260

261+
export function connectionTargetFromInput(input: string): ConnectionTarget {
262+
return isHttpUrl(input) ? { mcpUrl: input } : { server: input }
263+
}
264+
258265
function buildConnectionBody(
259-
mcpUrl: string | undefined,
266+
target: string | ConnectionTarget | undefined,
260267
options: ConnectionWriteOptions,
261-
): Record<string, unknown> {
262-
const body = {
263-
...(mcpUrl ? { mcpUrl } : {}),
268+
): ConnectionCreateParams {
269+
return {
270+
...normalizeConnectionTarget(target),
264271
...(options.name ? { name: options.name } : {}),
265272
...(options.metadata ? { metadata: options.metadata } : {}),
266273
...(options.headers ? { headers: options.headers } : {}),
267274
...(options.transport ? { transport: options.transport } : {}),
268275
}
269-
return body
270276
}
271277

272278
function buildConnectionSetParams(
273279
namespace: string,
274-
mcpUrl: string | undefined,
280+
target: string | ConnectionTarget | undefined,
275281
options: ConnectionWriteOptions,
276-
) {
282+
): ConnectionSetParams {
277283
return {
278284
namespace,
279-
...buildConnectionBody(mcpUrl, options),
285+
...buildConnectionBody(target, options),
280286
}
281287
}
282288

289+
function normalizeConnectionTarget(
290+
target: string | ConnectionTarget | undefined,
291+
): Pick<ConnectionCreateParams, "mcpUrl" | "server"> {
292+
if (!target) return {}
293+
if (typeof target === "string") return { mcpUrl: target }
294+
return target
295+
}
296+
297+
function isHttpUrl(value: string): boolean {
298+
return value.startsWith("http://") || value.startsWith("https://")
299+
}
300+
283301
function namespacePath(namespace: string): string {
284302
return `/${encodeURIComponent(namespace)}`
285303
}

src/commands/mcp/normalize-url.ts

Lines changed: 0 additions & 17 deletions
This file was deleted.

0 commit comments

Comments
 (0)