-
Notifications
You must be signed in to change notification settings - Fork 54
feat: Add deployment to smithery #235
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
jirispilka
wants to merge
12
commits into
master
Choose a base branch
from
feat/smithery
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from 7 commits
Commits
Show all changes
12 commits
Select commit
Hold shift + click to select a range
07bb3ae
feat: Add deployment to smithery
jirispilka 8ffbc5f
fix: node:24
jirispilka cca4a46
fix: I've become smithery master :D
jirispilka 998cb90
fix: smithery
jirispilka 4cd7c4a
fix: smithery
jirispilka 688337c
fix: smithery, review comments
jirispilka 22ae208
fix: lint
jirispilka b58a4cf
Update src/smithery.ts
jirispilka 7022106
Merge branch 'master' into feat/smithery
jirispilka 808650b
fix: bad merge
jirispilka aecd9f0
fix: Update smithery.md
jirispilka a72109f
fix: Add comment
jirispilka File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
FROM node:24-alpine AS builder | ||
|
||
WORKDIR /app | ||
|
||
COPY package.json package-lock.json ./ | ||
|
||
# Install all dependencies (including devDependencies for build) | ||
RUN npm ci --ignore-scripts | ||
|
||
COPY src ./src | ||
COPY tsconfig.json ./ | ||
|
||
RUN npm run build | ||
|
||
FROM node:24-alpine AS release | ||
|
||
WORKDIR /app | ||
|
||
# Copy only the necessary files from the build stage | ||
COPY --from=builder /app/dist ./dist | ||
COPY package.json package-lock.json ./ | ||
|
||
ENV NODE_ENV=production | ||
ENV APIFY_TOKEN=your-api-key-here | ||
|
||
# Install production dependencies only | ||
RUN npm ci --ignore-scripts --omit=dev | ||
|
||
# Set the entry point for the container | ||
ENTRYPOINT ["node", "dist/smithery.js"] |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
# Smithery integration | ||
|
||
- The Smithery entrypoint is `src/smithery.ts`. | ||
- It exports `configSchema` and a default sync function returning the MCP server instance. | ||
- On startup, if `apifyToken`/`APIFY_TOKEN` is provided, tools load asynchronously and the first `listTools` is gated via a one-time barrier (`blockListToolsUntil`). | ||
- If no token is provided, tools are loaded with placeholder token `PLACEHOLDER_TOKEN` to allow the server to start without real secrets. | ||
|
||
Run with Smithery: | ||
|
||
```bash | ||
npx @smithery/cli build | ||
# optional, recommended for actors | ||
export APIFY_TOKEN="your-apify-token" | ||
npx @smithery/cli dev | ||
``` | ||
|
||
Notes: | ||
- The barrier is used only by Smithery; stdio/SSE/HTTP flows are unaffected. | ||
- We use a placeholder token (`your-apify-token`) in non-interactive environments (Smithery scans) to allow tool-loading paths to run without real secrets. It does not grant access; when detected, the client runs unauthenticated to let the server start and list tools where possible. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,17 +1,6 @@ | ||
# Smithery configuration file: https://smithery.ai/docs/config#smitheryyaml | ||
|
||
runtime: "container" | ||
build: | ||
dockerfile: "Dockerfile.smithery" | ||
dockerBuildPath: "." | ||
startCommand: | ||
type: stdio | ||
configSchema: | ||
# JSON Schema defining the configuration options for the MCP. | ||
type: object | ||
required: | ||
- apifyToken | ||
properties: | ||
apifyToken: | ||
type: string | ||
description: The API token for accessing Apify's services. | ||
commandFunction: | ||
# A function that produces the CLI command to start the MCP on stdio. | ||
|- | ||
(config) => ({ command: 'node', args: ['dist/main.js'], env: { APIFY_TOKEN: config.apifyToken } }) | ||
type: "stdio" |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,8 +1,6 @@ | ||
#!/usr/bin/env node | ||
/* | ||
This file provides essential functions and tools for MCP servers, serving as a library. | ||
The ActorsMcpServer should be the only class exported from the package | ||
*/ | ||
|
||
import { ActorsMcpServer } from './mcp/server.js'; | ||
|
||
export { ActorsMcpServer }; | ||
export { ActorsMcpServer } from './mcp/server.js'; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
#!/usr/bin/env node | ||
/* | ||
This file provides essential functions and tools for MCP servers, serving as a library. | ||
The ActorsMcpServer should be the only class exported from the package | ||
Also, it serves as the main entry point for smithery deployment. | ||
*/ | ||
jirispilka marked this conversation as resolved.
Show resolved
Hide resolved
|
||
import type { z } from 'zod'; | ||
|
||
import { PLACEHOLDER_APIFY_TOKEN } from './const.js'; | ||
import { ActorsMcpServer } from './mcp/server.js'; | ||
import type { Input, ToolCategory } from './types'; | ||
import { serverConfigSchemaSmithery as configSchema } from './types.js'; | ||
import { loadToolsFromInput } from './utils/tools-loader.js'; | ||
|
||
// Export the config schema for Smithery. The export must be named configSchema | ||
export { configSchema }; | ||
|
||
/** | ||
* The Main entrypoint for Smithery deployment do not change signature of this function. | ||
* @returns | ||
*/ | ||
// eslint-disable-next-line import/no-default-export | ||
export default function ({ config: _config }: { config: z.infer<typeof configSchema> }) { | ||
try { | ||
const apifyToken = _config.apifyToken || process.env.APIFY_TOKEN || ''; | ||
const enableAddingActors = _config.enableAddingActors ?? true; | ||
const actors = _config.actors || ''; | ||
const actorList = actors ? actors.split(',').map((a: string) => a.trim()) : []; | ||
const toolCategoryKeys = _config.tools ? _config.tools.split(',').map((t: string) => t.trim()) : []; | ||
|
||
process.env.APIFY_TOKEN = apifyToken; // Ensure token is set in the environment | ||
const server = new ActorsMcpServer({ enableAddingActors, enableDefaultActors: false }); | ||
|
||
const input: Input = { | ||
actors: actorList.length ? actorList : [], | ||
enableAddingActors, | ||
tools: toolCategoryKeys as ToolCategory[], | ||
}; | ||
|
||
// Start async tools loading and gate the first listTools (Smithery-only) | ||
// See docs/smithery.md for a brief overview of how this entrypoint works with Smithery | ||
const loadPromise = (async () => { | ||
try { | ||
const tools = await loadToolsFromInput(input, apifyToken, actorList.length === 0); | ||
server.upsertTools(tools); | ||
} catch (error) { | ||
// eslint-disable-next-line no-console | ||
console.error('Failed to load tools with provided token. Retrying with placeholder token, error', error); | ||
try { | ||
const tools = await loadToolsFromInput(input, PLACEHOLDER_APIFY_TOKEN, actorList.length === 0); | ||
server.upsertTools(tools); | ||
} catch (retryError) { | ||
// eslint-disable-next-line no-console | ||
console.error('Retry failed to load tools with placeholder token, error:', retryError); | ||
} | ||
} | ||
})(); | ||
server.blockListToolsUntil(loadPromise); | ||
return server.server; | ||
} catch (e) { | ||
// eslint-disable-next-line no-console | ||
console.error(e); | ||
throw e; | ||
} | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
import { beforeEach, describe, expect, it, vi } from 'vitest'; | ||
|
||
import log from '@apify/log'; | ||
|
||
import { ActorsMcpServer } from '../../src/mcp/server.js'; | ||
import smithery from '../../src/smithery.js'; | ||
import * as toolsLoader from '../../src/utils/tools-loader.js'; | ||
|
||
// Silence logs in unit tests | ||
log.setLevel(log.LEVELS.OFF); | ||
|
||
describe('smithery entrypoint barrier behavior', () => { | ||
beforeEach(() => { | ||
vi.restoreAllMocks(); | ||
}); | ||
|
||
it('calls blockListToolsUntil', async () => { | ||
// Arrange | ||
const blockSpy = vi.spyOn(ActorsMcpServer.prototype, 'blockListToolsUntil'); | ||
const loadSpy = vi.spyOn(toolsLoader, 'loadToolsFromInput').mockResolvedValue([]); | ||
|
||
// Act | ||
const server = smithery({ config: { apifyToken: 'TEST_TOKEN', enableAddingActors: true, enableActorAutoLoading: true } }); | ||
|
||
// Assert | ||
expect(server).toBeTruthy(); | ||
expect(blockSpy).toHaveBeenCalledTimes(1); | ||
expect(loadSpy).toHaveBeenCalledTimes(1); | ||
}); | ||
}); |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Info I get from LLM how to use main and module:
The problem is that we have other exported APIs in the main and module. I think it will break our internal server if Typescript looks at the module and sees only Smithery, not ActorsMcpServer.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm sorry for the late answer, as smithery deployment was deprioritized.
I'll fix it based on your comment.
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It does not work at smithery, when I use:
Because smithery is not building the project 🤷🏻
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If some settings in Smithery are possible (like changing the root directory), we can create a directory with the package only for Smithery.
It looks like Smithery requirements are not compatible with normal package usage.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll try to deploy remote server, if possible