Skip to content
Open
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
2 changes: 1 addition & 1 deletion packages/core/src/execution/__tests__/rate-limit.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { describe, it, expect, beforeEach } from 'vitest';
import { z } from 'zod';
import { executeNode } from '../execute-node.js';
import { MemoryRateLimitStore } from '../memory-rate-limit-store.js';
Expand Down
2 changes: 1 addition & 1 deletion packages/docs-mcp/LICENSE
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
MIT License

Copyright (c) 2026 KNQuoc
Copyright (c) 2026 Jam

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
Expand Down
49 changes: 13 additions & 36 deletions packages/docs-mcp/README.md
Original file line number Diff line number Diff line change
@@ -1,31 +1,21 @@
# jam-nodes-docs-mcp

MCP (Model Context Protocol) server that exposes [jam-nodes](https://github.com/KNQuoc/jam-nodes) documentation as tools, so AI agents can query docs contextually without loading everything into context.
MCP (Model Context Protocol) server that exposes [jam-nodes](https://github.com/wespreadjam/jam-nodes) documentation as tools, so AI agents can query docs contextually without loading everything into context.

## Tools

| Tool | Description |
|------|-------------|
| `search_docs` | Keyword search across all documentation |
| `get_node_info` | Full docs for a specific node by type (e.g. `http_request`) |
| `list_nodes` | List all 16 built-in nodes with descriptions |
| `get_api_reference` | API docs for core types, registry, execution context, editor |
| `list_nodes` | List all built-in nodes with descriptions |
| `get_api_reference` | API docs for core types, registry, execution context |
| `get_guide` | Get the creating-nodes guide or quick start |

## Installation

```bash
npm install -g jam-nodes-docs-mcp
```

Or run directly:

```bash
npx jam-nodes-docs-mcp
```

## Configuration

This package is not yet published to npm. To use it, build locally first.

### Claude Desktop

Add to `claude_desktop_config.json`:
Expand All @@ -34,8 +24,8 @@ Add to `claude_desktop_config.json`:
{
"mcpServers": {
"jam-nodes-docs": {
"command": "npx",
"args": ["jam-nodes-docs-mcp"]
"command": "node",
"args": ["<path-to-repo>/packages/docs-mcp/dist/index.js"]
}
}
}
Expand All @@ -49,34 +39,21 @@ Add to `.cursor/mcp.json`:
{
"mcpServers": {
"jam-nodes-docs": {
"command": "npx",
"args": ["jam-nodes-docs-mcp"]
"command": "node",
"args": ["<path-to-repo>/packages/docs-mcp/dist/index.js"]
}
}
}
```

### OpenClaw

Add to your MCP config:

```json
{
"jam-nodes-docs": {
"command": "npx",
"args": ["jam-nodes-docs-mcp"]
}
}
```

## Development

```bash
npm install
npm run build
npm start
pnpm install
pnpm build
node dist/index.js
```

## License

MIT © KNQuoc
MIT — see root [LICENSE](../../LICENSE)
2 changes: 1 addition & 1 deletion packages/docs-mcp/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
"start": "node dist/index.js"
},
"keywords": ["mcp", "jam-nodes", "documentation", "ai"],
"author": "KNQuoc",
"author": "wespreadjam",
"license": "MIT",
"dependencies": {
"@modelcontextprotocol/sdk": "^1.0.0"
Expand Down
51 changes: 49 additions & 2 deletions packages/docs-mcp/src/docs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,9 @@ jam-nodes is an extensible, type-safe workflow node framework for building autom
\`\`\`
@jam-nodes/core → Types, NodeRegistry, ExecutionContext, defineNode utility
@jam-nodes/nodes → Built-in node definitions (logic, transform, AI, integrations)
@jam-nodes/editor → React visual editor, WorkflowRunner, schema introspection
\`\`\`

**core** is dependency-free (except Zod + jsonpath-plus). **nodes** depends on core. **editor** depends on core + React + @xyflow/react.
**core** is dependency-free (except Zod + jsonpath-plus). **nodes** depends on core.

## Quick Start

Expand Down Expand Up @@ -729,19 +728,67 @@ export interface NodeInfo {
}

const NODE_TYPES: NodeInfo[] = [
// Logic
{ type: 'conditional', name: 'Conditional', category: 'logic', description: 'Branch workflow based on a condition', fullDoc: '' },
{ type: 'end', name: 'End', category: 'logic', description: 'Mark the end of a workflow branch', fullDoc: '' },
{ type: 'delay', name: 'Delay', category: 'logic', description: 'Pause workflow execution for a specified duration', fullDoc: '' },
{ type: 'webhook_trigger', name: 'Webhook Trigger', category: 'logic', description: 'Receive and validate incoming webhook payloads', fullDoc: '' },
// Transform
{ type: 'map', name: 'Map', category: 'transform', description: 'Extract a property from each item in an array', fullDoc: '' },
{ type: 'filter', name: 'Filter', category: 'transform', description: 'Filter items in an array based on a condition', fullDoc: '' },
{ type: 'sort', name: 'Sort', category: 'transform', description: 'Sort array items by a property', fullDoc: '' },
// Examples
{ type: 'http_request', name: 'HTTP Request', category: 'integration', description: 'Make HTTP requests to external APIs', fullDoc: '' },
{ type: 'bread', name: 'Bread', category: 'integration', description: 'Simple demo node returning a greeting', fullDoc: '' },

Copilot AI Apr 4, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The bread node metadata here doesn’t match the actual node definition: in packages/nodes/src/examples/bread.ts it’s categorized as action and described as always outputting "bread". Since list_nodes relies on this list, please update this entry’s category/description (and any other mismatches) to reflect the real node metadata.

Suggested change
{ type: 'bread', name: 'Bread', category: 'integration', description: 'Simple demo node returning a greeting', fullDoc: '' },
{ type: 'bread', name: 'Bread', category: 'action', description: 'Always outputs "bread"', fullDoc: '' },

Copilot uses AI. Check for mistakes.
// Social
{ type: 'reddit_monitor', name: 'Reddit Monitor', category: 'integration', description: 'Search Reddit for posts matching keywords', fullDoc: '' },
{ type: 'twitter_monitor', name: 'Twitter Monitor', category: 'integration', description: 'Search Twitter/X for posts matching keywords', fullDoc: '' },
{ type: 'twitter_create_tweet', name: 'Twitter Create Tweet', category: 'integration', description: 'Post a new tweet on Twitter/X', fullDoc: '' },
{ type: 'twitter_delete_tweet', name: 'Twitter Delete Tweet', category: 'integration', description: 'Delete a tweet by ID', fullDoc: '' },
{ type: 'twitter_like_tweet', name: 'Twitter Like Tweet', category: 'integration', description: 'Like a tweet by ID', fullDoc: '' },
{ type: 'twitter_retweet', name: 'Twitter Retweet', category: 'integration', description: 'Retweet a tweet by ID', fullDoc: '' },
{ type: 'twitter_search_tweets', name: 'Twitter Search Tweets', category: 'integration', description: 'Search tweets by query', fullDoc: '' },
{ type: 'twitter_send_dm', name: 'Twitter Send DM', category: 'integration', description: 'Send a direct message on Twitter/X', fullDoc: '' },
{ type: 'twitter_get_user_by_username', name: 'Twitter Get User', category: 'integration', description: 'Look up a Twitter/X user by username', fullDoc: '' },

Copilot AI Apr 4, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

twitter_get_user_by_username’s displayed name/description here are slightly inconsistent with the node’s actual metadata (name: "Twitter Get User By Username", description: "Get a Twitter/X user profile by username"). Consider matching the real node metadata to avoid confusion in list_nodes output.

Suggested change
{ type: 'twitter_get_user_by_username', name: 'Twitter Get User', category: 'integration', description: 'Look up a Twitter/X user by username', fullDoc: '' },
{ type: 'twitter_get_user_by_username', name: 'Twitter Get User By Username', category: 'integration', description: 'Get a Twitter/X user profile by username', fullDoc: '' },

Copilot uses AI. Check for mistakes.
{ type: 'linkedin_monitor', name: 'LinkedIn Monitor', category: 'integration', description: 'Search LinkedIn for posts via ForumScout API', fullDoc: '' },
// OpenAI
{ type: 'sora_video', name: 'Sora Video', category: 'integration', description: 'Generate AI video using OpenAI Sora 2', fullDoc: '' },
// DataForSEO
{ type: 'seo_keyword_research', name: 'SEO Keyword Research', category: 'integration', description: 'Research keywords with search volume, difficulty, CPC, and intent data', fullDoc: '' },
{ type: 'seo_audit', name: 'SEO Audit', category: 'integration', description: 'Run comprehensive SEO audit on a URL', fullDoc: '' },
{ type: 'dataforseo_get_backlinks', name: 'DataForSEO Backlinks', category: 'integration', description: 'Get backlink data for a domain or URL', fullDoc: '' },
{ type: 'dataforseo_people_also_ask', name: 'DataForSEO People Also Ask', category: 'integration', description: 'Get People Also Ask questions for a keyword', fullDoc: '' },
{ type: 'dataforseo_serp', name: 'DataForSEO SERP', category: 'integration', description: 'Get SERP results for a keyword', fullDoc: '' },
// Apollo
{ type: 'search_contacts', name: 'Search Contacts', category: 'integration', description: 'Search for contacts using Apollo.io with email enrichment', fullDoc: '' },
// Discord
{ type: 'discord_send_message', name: 'Discord Send Message', category: 'integration', description: 'Send a message to a Discord channel via bot', fullDoc: '' },
{ type: 'discord_send_webhook', name: 'Discord Send Webhook', category: 'integration', description: 'Send a message via Discord webhook', fullDoc: '' },
{ type: 'discord_create_thread', name: 'Discord Create Thread', category: 'integration', description: 'Create a thread in a Discord channel', fullDoc: '' },
// Firecrawl
{ type: 'firecrawl_scrape', name: 'Firecrawl Scrape', category: 'integration', description: 'Scrape a single URL with AI-powered extraction', fullDoc: '' },
{ type: 'firecrawl_crawl', name: 'Firecrawl Crawl', category: 'integration', description: 'Crawl a website and extract content from multiple pages', fullDoc: '' },
{ type: 'firecrawl_extract', name: 'Firecrawl Extract', category: 'integration', description: 'Extract structured data from URLs using a schema', fullDoc: '' },
// Dev.to
{ type: 'devto_create_article', name: 'Dev.to Create Article', category: 'integration', description: 'Create a new article on Dev.to', fullDoc: '' },
{ type: 'devto_update_article', name: 'Dev.to Update Article', category: 'integration', description: 'Update an existing article on Dev.to', fullDoc: '' },
{ type: 'devto_get_articles', name: 'Dev.to Get Articles', category: 'integration', description: 'Get published articles from Dev.to', fullDoc: '' },
// WordPress
{ type: 'wordpress_create_post', name: 'WordPress Create Post', category: 'integration', description: 'Create a new WordPress post', fullDoc: '' },
{ type: 'wordpress_update_post', name: 'WordPress Update Post', category: 'integration', description: 'Update an existing WordPress post', fullDoc: '' },
{ type: 'wordpress_get_posts', name: 'WordPress Get Posts', category: 'integration', description: 'Get posts from a WordPress site', fullDoc: '' },
{ type: 'wordpress_upload_media', name: 'WordPress Upload Media', category: 'integration', description: 'Upload media to a WordPress site', fullDoc: '' },
// Google Sheets
{ type: 'googleSheetsAppend', name: 'Google Sheets Append', category: 'integration', description: 'Append rows to a Google Sheets spreadsheet', fullDoc: '' },
{ type: 'googleSheetsClear', name: 'Google Sheets Clear', category: 'integration', description: 'Clear a range in a Google Sheets spreadsheet', fullDoc: '' },
{ type: 'googleSheetsRead', name: 'Google Sheets Read', category: 'integration', description: 'Read data from a Google Sheets spreadsheet', fullDoc: '' },
{ type: 'googleSheetsUpdate', name: 'Google Sheets Update', category: 'integration', description: 'Update cells in a Google Sheets spreadsheet', fullDoc: '' },
// Slack
{ type: 'slack_send_message', name: 'Slack Send Message', category: 'integration', description: 'Send a message to a Slack channel', fullDoc: '' },
{ type: 'slack_update_message', name: 'Slack Update Message', category: 'integration', description: 'Update an existing Slack message', fullDoc: '' },
{ type: 'slack_get_channel_history', name: 'Slack Get Channel History', category: 'integration', description: 'Get message history from a Slack channel', fullDoc: '' },
{ type: 'slack_search_messages', name: 'Slack Search Messages', category: 'integration', description: 'Search messages across Slack workspace', fullDoc: '' },
// AI
{ type: 'social_keyword_generator', name: 'Social Keyword Generator', category: 'action', description: 'Generate platform-specific search keywords from a topic using Claude', fullDoc: '' },
{ type: 'draft_emails', name: 'Draft Emails', category: 'action', description: 'Generate personalized email drafts for contacts using Claude', fullDoc: '' },
{ type: 'social_ai_analyze', name: 'Social AI Analyze', category: 'action', description: 'Analyze social media posts for relevance, sentiment, complaints, and urgency', fullDoc: '' },
Expand Down
4 changes: 2 additions & 2 deletions packages/docs-mcp/src/tools.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,15 +36,15 @@ export const TOOLS: Tool[] = [
},
{
name: 'list_nodes',
description: 'List all 16 built-in jam-nodes nodes with their type, name, category, and brief description.',
description: 'List all built-in jam-nodes nodes with their type, name, category, and brief description.',
inputSchema: {
type: 'object',
properties: {},
},
},
{
name: 'get_api_reference',
description: 'Get jam-nodes API reference for a specific area. Areas: "core", "types", "registry", "execution-context", "editor", "workflow-runner", "schema-introspector", "nodes".',
description: 'Get jam-nodes API reference for a specific area. Areas: "core", "types", "registry", "execution-context", "nodes".',
inputSchema: {
Comment on lines 46 to 48

Copilot AI Apr 4, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

get_api_reference description now advertises only core/types/registry/execution-context/nodes, but the runtime error message still suggests editor/workflow-runner/schema-introspector, and getApiReference() still appears to support those areas. Align the tool description, supported areas, and error messaging so users aren’t guided toward unsupported/removed areas (either remove the editor-related areas from implementation + error text, or add them back to the description).

Copilot uses AI. Check for mistakes.
type: 'object',
properties: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,6 @@ export const seoKeywordResearchNode = defineNode({
}
} catch (kwError) {
// Continue with other keywords on individual failures
console.warn(`Error researching keyword "${seedKeyword}":`, kwError);
}
Comment on lines 195 to 197

Copilot AI Apr 4, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This catch block now silently swallows per-keyword failures. With the console.warn removed, the node can return success: true with an empty/partial keywords list and no indication anything failed. Consider recording per-keyword errors in the output (or using the project’s logging mechanism) so users can distinguish “no results” from “requests failed.”

Copilot uses AI. Check for mistakes.
}

Expand Down
Loading