Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
7 changes: 7 additions & 0 deletions .changeset/ai-mcp-carve-out.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
"@gemstack/ai-sdk": minor
---

Remove the `./mcp` subpath. The agent<->MCP bridge (`mcpClientTools` / `mcpServerFromAgent`) has moved to its own package, `@gemstack/ai-mcp`, so the optional `@modelcontextprotocol/sdk` peer dependency is now declared only by the package that uses it (and no longer surfaces to every `@gemstack/ai-sdk` consumer).

Migration: replace `@gemstack/ai-sdk/mcp` imports with `@gemstack/ai-mcp`, and move the `@modelcontextprotocol/sdk` peer to that package. The bridge API is unchanged.
10 changes: 10 additions & 0 deletions .changeset/ai-mcp-initial.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
---
"@gemstack/ai-mcp": minor
---

Initial release. The agent<->MCP bridge, carved out of `@gemstack/ai-sdk`'s former `./mcp` subpath:

- `mcpClientTools(transport, opts?)` — consume a remote MCP server's tools as `@gemstack/ai-sdk` Agent tools (HTTP URL / stdio spawn / connected SDK client).
- `mcpServerFromAgent(AgentClass, opts?)` — expose an Agent as an MCP server, with `'tools'` / `'agent'` / `'both'` exposure modes.

Depends on `@gemstack/ai-sdk`; `@modelcontextprotocol/sdk` is an optional peer.
58 changes: 58 additions & 0 deletions packages/ai-mcp/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
# @gemstack/ai-mcp

The bridge between [`@gemstack/ai-sdk`](https://github.com/gemstack-land/gemstack/tree/main/packages/ai-sdk) Agents and [Model Context Protocol](https://modelcontextprotocol.io) servers. Two connectors:

- **`mcpClientTools(transport, opts?)`** — consume a remote MCP server's tools as Agent tools.
- **`mcpServerFromAgent(AgentClass, opts?)`** — expose an Agent as an MCP server external clients (Claude Desktop, Cursor, etc.) can call.

This is the **agent bridge** axis of MCP. It depends on `@gemstack/ai-sdk` and is useless without an Agent. It was carved out of `@gemstack/ai-sdk`'s `/mcp` subpath so the optional MCP SDK dependency is declared only by the package that actually needs it.

## Which MCP package do I use?

> **Exposing an existing Agent, or feeding remote MCP tools into one?** Use `@gemstack/ai-mcp` (this package).
> **Authoring an MCP server from scratch** (tools / resources / prompts / auth)? Use a standalone MCP server framework — that is a separate, agent-agnostic concern, not this bridge.

Both can "produce an MCP server", but from different inputs: `mcpServerFromAgent(anAgent)` versus a hand-authored server. That overlap is expected, not duplication.

## Installation

```bash
pnpm add @gemstack/ai-mcp @modelcontextprotocol/sdk
```

`@modelcontextprotocol/sdk` is an **optional peer dependency** — install it when you use this bridge. `@gemstack/ai-sdk` comes in as a regular dependency.

## Usage

### Consume a remote MCP server's tools

```ts
import { mcpClientTools } from '@gemstack/ai-mcp'

// (a) HTTP — string URL or URL instance
const tools = await mcpClientTools('https://api.example.com/mcp')

// (b) Local stdio subprocess
const tools = await mcpClientTools({ command: 'npx', args: ['some-mcp-server'] })

// (c) Already-connected SDK Client (caller owns lifecycle)
const tools = await mcpClientTools(myClient)

// Spread into your Agent's tools(). Call tools.close() when done (cases a + b).
```

### Expose an Agent as an MCP server

```ts
import { mcpServerFromAgent } from '@gemstack/ai-mcp'
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'

const server = await mcpServerFromAgent(MyAgent)
await server.connect(new StdioServerTransport())
```

Three exposure modes via `opts.expose`: `'tools'` (default, one MCP tool per `agent.tools()` entry), `'agent'` (one tool that runs the whole agent: `prompt(text) → text`), or `'both'`.

## License

MIT
66 changes: 66 additions & 0 deletions packages/ai-mcp/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
{
"name": "@gemstack/ai-mcp",
"version": "0.0.0",
"description": "Bridge between @gemstack/ai-sdk Agents and Model Context Protocol servers: consume remote MCP tools as Agent tools, and expose an Agent as an MCP server.",
"keywords": [
"ai",
"agent",
"agents",
"mcp",
"model-context-protocol",
"tools",
"tool-calling",
"bridge",
"gemstack"
],
"license": "MIT",
"homepage": "https://github.com/gemstack-land/gemstack/tree/main/packages/ai-mcp#readme",
"bugs": {
"url": "https://github.com/gemstack-land/gemstack/issues"
},
"repository": {
"type": "git",
"url": "https://github.com/gemstack-land/gemstack",
"directory": "packages/ai-mcp"
},
"type": "module",
"engines": {
"node": ">=22.12.0"
},
"files": [
"dist"
],
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
"exports": {
".": {
"import": "./dist/index.js",
"types": "./dist/index.d.ts"
}
},
"scripts": {
"build": "tsc -p tsconfig.build.json",
"dev": "tsc -p tsconfig.build.json --watch",
"typecheck": "tsc --noEmit",
"test": "tsc -p tsconfig.test.json && cd dist-test && node --test",
"clean": "rm -rf dist"
},
"dependencies": {
"@gemstack/ai-sdk": "workspace:^",
"zod": "^4.0.0"
},
"peerDependencies": {
"@modelcontextprotocol/sdk": "^1.29.0"
},
"peerDependenciesMeta": {
"@modelcontextprotocol/sdk": {
"optional": true
}
},
"devDependencies": {
"@modelcontextprotocol/sdk": "^1.29.0",
"@types/node": "^20.0.0",
"typescript": "^5.4.0"
},
"author": "Suleiman Shahbari"
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ import { z } from 'zod'
import { Client } from '@modelcontextprotocol/sdk/client/index.js'
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'
import { InMemoryTransport } from '@modelcontextprotocol/sdk/inMemory.js'
import { mcpClientTools } from './mcp/client-tools.js'
import { toolToSchema } from './tool.js'
import { mcpClientTools } from './client-tools.js'
import { toolToSchema } from '@gemstack/ai-sdk'

// ─── Helpers ─────────────────────────────────────────────

Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { z } from 'zod'
import { dynamicTool } from '../tool.js'
import type { Tool, ToolCallContext } from '../types.js'
import { dynamicTool } from '@gemstack/ai-sdk'
import type { Tool, ToolCallContext } from '@gemstack/ai-sdk'
import type {
McpClientTransport, McpClientToolsOptions, StdioServerSpawn,
} from './types.js'

const CLIENT_INFO = { name: 'rudderjs-ai-mcp-bridge', version: '1.0.0' } as const
const CLIENT_INFO = { name: 'gemstack-ai-mcp-bridge', version: '1.0.0' } as const

/**
* The result of `mcpClientTools()` — an array of `Tool`s that also carries a
Expand All @@ -21,7 +21,7 @@ export interface McpClientToolsHandle extends ReadonlyArray<Tool> {
}

/**
* Connect to a remote MCP server and surface its tools as Rudder `Tool`s.
* Connect to a remote MCP server and surface its tools as `@gemstack/ai-sdk` `Tool`s.
*
* Three transport shapes are accepted:
*
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* `@gemstack/ai-sdk/mcp` — bridge between `@gemstack/ai-sdk` Agents and Model Context
* `@gemstack/ai-mcp` — bridge between `@gemstack/ai-sdk` Agents and Model Context
* Protocol servers. Two connectors:
*
* - {@link mcpClientTools} — consume a remote MCP server's tools as Agent tools
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,11 @@ import assert from 'node:assert/strict'
import { z } from 'zod'
import { Client } from '@modelcontextprotocol/sdk/client/index.js'
import { InMemoryTransport } from '@modelcontextprotocol/sdk/inMemory.js'
import { Agent } from './agent.js'
import { AiRegistry } from './registry.js'
import { toolDefinition } from './tool.js'
import { mcpServerFromAgent } from './mcp/server-from-agent.js'
import { Agent, AiRegistry, toolDefinition } from '@gemstack/ai-sdk'
import { mcpServerFromAgent } from './server-from-agent.js'
import type {
AiMessage, ProviderAdapter, ProviderRequestOptions, ProviderResponse, StreamChunk,
} from './types.js'
} from '@gemstack/ai-sdk'

// ─── Scripted adapter (copy of handoff.test.ts pattern) ───

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,8 @@ import assert from 'node:assert/strict'
import { z } from 'zod'
import { Client } from '@modelcontextprotocol/sdk/client/index.js'
import { InMemoryTransport } from '@modelcontextprotocol/sdk/inMemory.js'
import { Agent } from './agent.js'
import { AiFake } from './fake.js'
import { AiRegistry } from './registry.js'
import { toolDefinition } from './tool.js'
import { mcpServerFromAgent } from './mcp/server-from-agent.js'
import { Agent, AiFake, AiRegistry, toolDefinition } from '@gemstack/ai-sdk'
import { mcpServerFromAgent } from './server-from-agent.js'

// ─── Fixture agent ────────────────────────────────────────

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { z } from 'zod'
import type { Agent } from '../agent.js'
import type { HasTools, Tool, ToolCallContext } from '../types.js'
import type { Agent } from '@gemstack/ai-sdk'
import type { HasTools, Tool, ToolCallContext } from '@gemstack/ai-sdk'
import type { McpServerFromAgentOptions } from './types.js'

/**
Expand All @@ -11,7 +11,7 @@ import type { McpServerFromAgentOptions } from './types.js'
* SDK's stdio / HTTP transports:
*
* ```ts
* import { mcpServerFromAgent } from '@gemstack/ai-sdk/mcp'
* import { mcpServerFromAgent } from '@gemstack/ai-mcp'
* import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'
*
* const server = await mcpServerFromAgent(MyAgent)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* Public types for `@gemstack/ai-sdk/mcp`. Kept in a separate module so the
* Public types for `@gemstack/ai-mcp`. Kept in a separate module so the
* client + server connectors can share them without circular imports.
*/

Expand Down
6 changes: 6 additions & 0 deletions packages/ai-mcp/tsconfig.build.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"extends": "../../tsconfig.base.json",
"compilerOptions": { "outDir": "dist", "rootDir": "src" },
"include": ["src"],
"exclude": ["src/**/*.test.ts"]
}
5 changes: 5 additions & 0 deletions packages/ai-mcp/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"extends": "../../tsconfig.base.json",
"compilerOptions": { "noEmit": true, "rootDir": "src" },
"include": ["src"]
}
5 changes: 5 additions & 0 deletions packages/ai-mcp/tsconfig.test.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"extends": "../../tsconfig.base.json",
"compilerOptions": { "outDir": "dist-test", "rootDir": "src" },
"include": ["src"]
}
5 changes: 3 additions & 2 deletions packages/ai-sdk/README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# @gemstack/ai-sdk

AI engine: providers, agents, tools, streaming, middleware, structured output, conversation memory, evals, MCP, computer-use, and testing fakes.
AI engine: providers, agents, tools, streaming, middleware, structured output, conversation memory, evals, computer-use, and testing fakes.

The first [GemStack](https://github.com/gemstack-land/gemstack) package. Spun out of Rudder's `@rudderjs/ai` (carried forward from the 1.17.x line, renamed and re-versioned under the GemStack umbrella). The Rudder package now ships as a thin deprecated re-export of this one.

Expand Down Expand Up @@ -40,14 +40,15 @@ The neutral storage contracts (`UserMemory`, `ConversationStore`, `BudgetStorage
| `.` | Core: `Agent`, `tool`, streaming, middleware, facade |
| `./server` | The server provider entry |
| `./node` | Node-only entry |
| `./mcp` | Model Context Protocol server/client helpers |
| `./computer-use` | Computer-use tool + executor |
| `./eval` | Eval framework (`evalSuite`, metrics, reporters) |
| `./gateway` | Gateway helpers |
| `./conversation-orm`, `./memory-orm`, `./budget-orm` | ORM-backed stores (optional `@rudderjs/orm` peer; moving behind the neutral seam) |
| `./memory-embedding` | Embedding-backed user memory |
| `./react` | React bindings |

> **Moved in `0.3.0`:** the MCP bridge (`mcpClientTools` / `mcpServerFromAgent`), previously the `./mcp` subpath, is now its own package, [`@gemstack/ai-mcp`](https://github.com/gemstack-land/gemstack/tree/main/packages/ai-mcp). Update `@gemstack/ai-sdk/mcp` imports to `@gemstack/ai-mcp` and move the `@modelcontextprotocol/sdk` peer there.

## License

MIT
11 changes: 0 additions & 11 deletions packages/ai-sdk/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,6 @@
"tools",
"tool-calling",
"streaming",
"mcp",
"model-context-protocol",
"computer-use",
"eval",
"structured-output",
Expand Down Expand Up @@ -73,10 +71,6 @@
"import": "./dist/observers.js",
"types": "./dist/observers.d.ts"
},
"./mcp": {
"import": "./dist/mcp/index.js",
"types": "./dist/mcp/index.d.ts"
},
"./chat-mentions": {
"import": "./dist/chat-mentions.js",
"types": "./dist/chat-mentions.d.ts"
Expand Down Expand Up @@ -125,7 +119,6 @@
"zod": "^4.0.0"
},
"peerDependencies": {
"@modelcontextprotocol/sdk": "^1.29.0",
"@rudderjs/console": "^1.4.3",
"@rudderjs/core": "^1.13.3",
"@rudderjs/orm": "^1.22.0",
Expand All @@ -141,9 +134,6 @@
"@rudderjs/orm": {
"optional": true
},
"@modelcontextprotocol/sdk": {
"optional": true
},
"react": {
"optional": true
}
Expand All @@ -156,7 +146,6 @@
"openai": ">=4.70.0"
},
"devDependencies": {
"@modelcontextprotocol/sdk": "^1.29.0",
"@rudderjs/console": "^1.4.3",
"@rudderjs/core": "^1.13.3",
"@rudderjs/orm": "^1.22.0",
Expand Down
18 changes: 17 additions & 1 deletion pnpm-lock.yaml

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

Loading