feat: mirror sweetistics mcp workflow
This commit is contained in:
parent
5744ce370f
commit
cfa2b8df50
@ -57,6 +57,10 @@ npx mcp-runtime list # list all configured servers
|
||||
npx mcp-runtime list vercel --schema # show tool signatures + schemas
|
||||
npx mcp-runtime call linear.searchIssues owner=ENG status=InProgress
|
||||
npx mcp-runtime call signoz.query --tail-log # print the tail of returned log files
|
||||
|
||||
# local scripts mirroring the Sweetistics workflow
|
||||
pnpm mcp:list # alias for mcp-runtime list
|
||||
pnpm mcp:call chrome-devtools.getTabs --tail-log
|
||||
```
|
||||
|
||||
Common flags:
|
||||
|
||||
84
config/mcp_servers.json
Normal file
84
config/mcp_servers.json
Normal file
@ -0,0 +1,84 @@
|
||||
[
|
||||
{
|
||||
"name": "chrome-devtools",
|
||||
"description": "Chrome DevTools protocol bridge for driving local tabs during debugging or automation.",
|
||||
"command": [
|
||||
"bash",
|
||||
"scripts/mcp_stdio_wrapper.sh",
|
||||
"env",
|
||||
"npm_config_loglevel=error",
|
||||
"npx",
|
||||
"-y",
|
||||
"chrome-devtools-mcp@latest"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "context7",
|
||||
"description": "Context7 MCP for React documentation lookup.",
|
||||
"command": "https://mcp.context7.com/mcp"
|
||||
},
|
||||
{
|
||||
"name": "linear",
|
||||
"description": "Hosted Linear MCP; exposes issue search, create, and workflow tooling.",
|
||||
"command": "https://mcp.linear.app/mcp",
|
||||
"headers": {
|
||||
"Authorization": "Bearer ${LINEAR_API_KEY}"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "next-devtools",
|
||||
"description": "Next.js dev server introspection (project metadata, logs, page insights).",
|
||||
"command": "http://localhost:3000/_next/mcp"
|
||||
},
|
||||
{
|
||||
"name": "playwright",
|
||||
"description": "Playwright MCP server for accessibility-driven automation.",
|
||||
"command": [
|
||||
"bash",
|
||||
"scripts/mcp_stdio_wrapper.sh",
|
||||
"env",
|
||||
"npm_config_loglevel=error",
|
||||
"npx",
|
||||
"-y",
|
||||
"@playwright/mcp@latest"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "vercel",
|
||||
"description": "Vercel MCP (requires OAuth).",
|
||||
"command": "https://mcp.vercel.com",
|
||||
"auth": "oauth",
|
||||
"token_cache_dir": "~/.cache/mcp-runtime/vercel"
|
||||
},
|
||||
{
|
||||
"name": "firecrawl",
|
||||
"description": "Firecrawl hosted MCP for authenticated scraping and extraction.",
|
||||
"command": "https://mcp.firecrawl.dev/v2/mcp",
|
||||
"headers": {
|
||||
"Authorization": "Bearer ${FIRECRAWL_API_KEY}"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "signoz",
|
||||
"description": "SigNoz Query MCP server (logs, traces, metrics).",
|
||||
"command": [
|
||||
"bash",
|
||||
"scripts/mcp_stdio_wrapper.sh",
|
||||
"env",
|
||||
"NODE_OPTIONS=--require=./scripts/mcp_signoz_retry_patch.cjs",
|
||||
"npm_config_loglevel=error",
|
||||
"npx",
|
||||
"-y",
|
||||
"signoz-mcp-server@latest"
|
||||
],
|
||||
"env": {
|
||||
"SIGNOZ_URL": "${SIGNOZ_URL:-http://localhost:3301}",
|
||||
"SIGNOZ_TOKEN": "${SIGNOZ_TOKEN:-}"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "shadcn",
|
||||
"description": "shadcn/ui registry MCP for browsing component recipes.",
|
||||
"command": "https://www.shadcn.io/api/mcp"
|
||||
}
|
||||
]
|
||||
52
docs/mcp.md
Normal file
52
docs/mcp.md
Normal file
@ -0,0 +1,52 @@
|
||||
---
|
||||
summary: 'Working with configured MCP servers via the mcp-runtime CLI and scripts.'
|
||||
---
|
||||
|
||||
# MCP Workflow
|
||||
|
||||
`mcp-runtime` reads server definitions from `config/mcp_servers.json`. Each entry mirrors the Sweetistics setup (headers, stdio wrappers, OAuth hints) so team members can lean on the same command surface area without Python dependencies.
|
||||
|
||||
## Scripts
|
||||
|
||||
```
|
||||
pnpm mcp:list [<name>] [--schema]
|
||||
pnpm mcp:call <server>.<tool> [key=value...] [--args '{"foo":"bar"}'] [--tail-log]
|
||||
```
|
||||
|
||||
Both scripts forward straight to the TypeScript CLI (`src/cli.ts`), so they support the same flags documented in the [README](../README.md).
|
||||
|
||||
- `pnpm mcp:list` – enumerate all configured servers; pass a specific name to inspect tool signatures.
|
||||
- `pnpm mcp:list <name> --schema` – dump the full JSON schema for each tool exposed by `<name>`.
|
||||
- `pnpm mcp:call` – execute a tool using either loose `key=value` pairs or `--args` JSON; append `--tail-log` to follow log files reported by the response.
|
||||
|
||||
## Adding or Updating Servers
|
||||
|
||||
1. Edit `config/mcp_servers.json` (keep the entries sorted alphabetically).
|
||||
2. Use `${ENV}` or `${ENV:-default}` placeholders for secrets; they are resolved at runtime.
|
||||
3. Set `auth: "oauth"` when the server requires an OAuth dance – the CLI spins up a local callback server and persists tokens under `~/.mcp-runtime/<name>/`.
|
||||
4. For stdio transports, wrap the command with `scripts/mcp_stdio_wrapper.sh` to inherit repo-relative paths, just like the Sweetistics helper.
|
||||
|
||||
After editing the config, you can validate the entry with:
|
||||
|
||||
```
|
||||
pnpm mcp:list <name>
|
||||
pnpm mcp:call <name>.<tool> --args '{"sample":true}'
|
||||
```
|
||||
|
||||
## Environment Variables
|
||||
|
||||
The CLI respects the same conventions as the original Python wrapper:
|
||||
|
||||
- `${LINEAR_API_KEY}`, `${FIRECRAWL_API_KEY}`, etc. for hosted servers.
|
||||
- `$env:VAR` to inject the raw runtime value without fallbacks.
|
||||
- `env` blocks per entry to provide default values (e.g., SigNoz URLs/tokens).
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
| Issue | Suggested Fix |
|
||||
| --- | --- |
|
||||
| OAuth flow never completes | Ensure the browser opened `http://127.0.0.1:<port>/callback`; copy the printed URL manually if not. |
|
||||
| Stdio command cannot find scripts | Pass `--root <repo>` or run from the project root so relative paths resolve. |
|
||||
| Tokens are stale | Delete `~/.mcp-runtime/<server>/tokens.json` and rerun the command. |
|
||||
|
||||
Refer to [`docs/spec.md`](./spec.md) for deeper architectural notes and future roadmap items.
|
||||
13
package.json
13
package.json
@ -21,12 +21,17 @@
|
||||
},
|
||||
"scripts": {
|
||||
"build": "tsc -p tsconfig.build.json",
|
||||
"check": "biome check",
|
||||
"check": "pnpm lint:biome && pnpm lint:oxlint && pnpm typecheck",
|
||||
"lint": "pnpm check",
|
||||
"lint:biome": "biome check",
|
||||
"lint:oxlint": "oxlint --type-aware --tsconfig tsconfig.json --max-warnings=0",
|
||||
"typecheck": "tsgo --project tsconfig.json --noEmit",
|
||||
"test": "vitest run",
|
||||
"clean": "rimraf dist",
|
||||
"dev": "tsc -w -p tsconfig.build.json",
|
||||
"prepublishOnly": "pnpm check && pnpm test && pnpm build"
|
||||
"prepublishOnly": "pnpm check && pnpm test && pnpm build",
|
||||
"mcp:list": "pnpm exec tsx src/cli.ts list",
|
||||
"mcp:call": "pnpm exec tsx src/cli.ts call"
|
||||
},
|
||||
"dependencies": {
|
||||
"@modelcontextprotocol/sdk": "^1.10.1",
|
||||
@ -36,8 +41,12 @@
|
||||
"@biomejs/biome": "^2.3.3",
|
||||
"@types/express": "^4.17.21",
|
||||
"@types/node": "^22.7.4",
|
||||
"@typescript/native-preview": "7.0.0-dev.20251104.1",
|
||||
"express": "^4.21.1",
|
||||
"oxlint": "^1.25.0",
|
||||
"oxlint-tsgolint": "^0.5.0",
|
||||
"rimraf": "^6.0.1",
|
||||
"tsx": "^4.16.5",
|
||||
"typescript": "^5.6.3",
|
||||
"vitest": "^1.6.0"
|
||||
},
|
||||
|
||||
9
scripts/mcp_signoz_retry_patch.cjs
Normal file
9
scripts/mcp_signoz_retry_patch.cjs
Normal file
@ -0,0 +1,9 @@
|
||||
// No-op hook retained for compatibility with the Sweetistics configuration.
|
||||
// Signoz MCP occasionally retries on transient errors; keeping this module
|
||||
// allows `NODE_OPTIONS=--require=./scripts/mcp_signoz_retry_patch.cjs` to load
|
||||
// without throwing if teams reuse the same entry.
|
||||
|
||||
module.exports = {
|
||||
// Export a no-op hook so consumers can keep requiring this patch file safely.
|
||||
onRetry() {},
|
||||
};
|
||||
9
scripts/mcp_stdio_wrapper.sh
Executable file
9
scripts/mcp_stdio_wrapper.sh
Executable file
@ -0,0 +1,9 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
if [[ $# -eq 0 ]]; then
|
||||
echo "mcp_stdio_wrapper.sh: missing command" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
exec "$@"
|
||||
@ -12,6 +12,9 @@ import { createRuntime } from "../src/runtime.js";
|
||||
|
||||
const app = express();
|
||||
app.use(express.json());
|
||||
app.get("/mcp", (_req, res) => {
|
||||
res.sendStatus(405);
|
||||
});
|
||||
|
||||
const server = new McpServer({
|
||||
name: "integration-demo",
|
||||
@ -42,18 +45,28 @@ server.registerResource(
|
||||
title: "Greeting",
|
||||
description: "Dynamic greeting resource",
|
||||
},
|
||||
async (uri, { name }) => ({
|
||||
contents: [
|
||||
{
|
||||
uri: uri.href,
|
||||
text: `Hello, ${name}!`,
|
||||
},
|
||||
],
|
||||
}),
|
||||
async (uri, { name }) => {
|
||||
const normalizedName =
|
||||
typeof name === "string"
|
||||
? name
|
||||
: Array.isArray(name)
|
||||
? name.join(", ")
|
||||
: "friend";
|
||||
|
||||
return {
|
||||
contents: [
|
||||
{
|
||||
uri: uri.href,
|
||||
text: `Hello, ${normalizedName}!`,
|
||||
},
|
||||
],
|
||||
};
|
||||
},
|
||||
);
|
||||
|
||||
app.post("/mcp", async (req, res) => {
|
||||
const transport = new StreamableHTTPServerTransport({
|
||||
sessionIdGenerator: undefined,
|
||||
enableJsonResponse: true,
|
||||
});
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user