Skip to content

Commit b4b6e4b

Browse files
authored
feat: add 1Password MCP server definition (#22)
* Add 1Password MCP server definition Community 1Password MCP server for accessing secrets from vaults. List vaults, retrieve items, and search credentials. Uses @takescake/1password-mcp. https://claude.ai/code/session_013AxurW8hPAfnUNvik354Xx * fix: update server definition (input type 'password' -> 'text' for OP_SERVICE_ACCOUNT_TOKEN) * fix: rename server files with transport suffix - community.1password.json -> community.1password-npx.json (id: community.1password-npx) Signed-off-by: Mohammod Al Amin Ashik <maa.ashik00@gmail.com> * fix: use GitHub org as ID domain for community definitions - community.1password-npx -> cakerepository.1password-mcp-npx Signed-off-by: Mohammod Al Amin Ashik <maa.ashik00@gmail.com> --------- Signed-off-by: Mohammod Al Amin Ashik <maa.ashik00@gmail.com>
1 parent 2588bc6 commit b4b6e4b

5 files changed

Lines changed: 684 additions & 0 deletions

File tree

fix_branches.py

Lines changed: 222 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,222 @@
1+
#!/usr/bin/env python3
2+
"""
3+
Process all claude/add-* branches:
4+
1. Rebase onto main
5+
2. Fix common issues in server definition JSON files
6+
3. Commit changes
7+
4. Push to new fix/* branch
8+
"""
9+
import subprocess
10+
import json
11+
import sys
12+
import os
13+
import re
14+
15+
REPO_DIR = r"d:\mcpmux\mcp-servers"
16+
17+
# Known icon fixes: MCP org icon -> actual project icon
18+
ICON_FIXES = {
19+
"community.filesystem": "https://avatars.githubusercontent.com/u/182288589?v=4", # keep MCP - it IS the MCP org project
20+
"community.puppeteer": "https://avatars.githubusercontent.com/u/182288589?v=4",
21+
"community.sqlite": "https://avatars.githubusercontent.com/u/182288589?v=4",
22+
"community.fetch": "https://avatars.githubusercontent.com/u/182288589?v=4",
23+
"community.memory": "https://avatars.githubusercontent.com/u/182288589?v=4",
24+
"community.sequential-thinking": "https://avatars.githubusercontent.com/u/182288589?v=4",
25+
"community.postgresql": "https://www.postgresql.org/media/img/about/press/elephant.png",
26+
"community.gitlab": "https://avatars.githubusercontent.com/u/1086321?v=4",
27+
"community.google-maps": "https://avatars.githubusercontent.com/u/1342004?v=4",
28+
}
29+
30+
# Known repo URL fixes
31+
REPO_FIXES = {
32+
"com.hubspot-mcp": "https://github.com/HubSpot/mcp-server",
33+
"com.resend-mcp": "https://github.com/resend/resend-mcp",
34+
"com.pagerduty-mcp": "https://github.com/PagerDuty/pagerduty-mcp-server",
35+
}
36+
37+
# Known doc URL fixes
38+
DOC_FIXES = {
39+
"community.postgresql": "https://github.com/modelcontextprotocol/servers/tree/main/src/postgres#readme",
40+
}
41+
42+
# Servers with known wrong packages
43+
PACKAGE_FIXES = {
44+
"com.pagerduty-mcp": {"args": ["pagerduty-mcp"]},
45+
}
46+
47+
# Stytch: repo URL is org page, should be removed or pointed to specific repo
48+
STYTCH_REPO = "https://github.com/stytchauth/stytch-mcp"
49+
50+
VALID_INPUT_TYPES = ["text", "number", "boolean", "url", "select", "file_path", "directory_path"]
51+
52+
def run(cmd, cwd=REPO_DIR, check=True):
53+
result = subprocess.run(cmd, cwd=cwd, capture_output=True, text=True, shell=True)
54+
if check and result.returncode != 0:
55+
return None, result.stderr.strip()
56+
return result.stdout.strip(), result.stderr.strip()
57+
58+
def fix_definition(filepath, server_id):
59+
"""Fix common issues in a server definition JSON file. Returns list of fixes applied."""
60+
with open(filepath, 'r', encoding='utf-8') as f:
61+
data = json.load(f)
62+
63+
fixes = []
64+
65+
# Fix invalid input types
66+
transport = data.get('transport', {})
67+
metadata = transport.get('metadata', {})
68+
inputs = metadata.get('inputs', [])
69+
70+
for inp in inputs:
71+
if inp.get('type') not in VALID_INPUT_TYPES:
72+
old_type = inp.get('type')
73+
inp['type'] = 'text'
74+
fixes.append(f"input type '{old_type}' -> 'text' for {inp['id']}")
75+
76+
# Fix MCP org icon for non-MCP-org projects
77+
icon = data.get('icon', '')
78+
if server_id in ICON_FIXES:
79+
new_icon = ICON_FIXES[server_id]
80+
if icon != new_icon:
81+
data['icon'] = new_icon
82+
fixes.append(f"icon updated")
83+
84+
# Fix known repo URLs
85+
if server_id in REPO_FIXES:
86+
links = data.get('links', {})
87+
if links.get('repository') != REPO_FIXES[server_id]:
88+
links['repository'] = REPO_FIXES[server_id]
89+
data['links'] = links
90+
fixes.append(f"repo URL fixed")
91+
92+
# Fix known doc URLs
93+
if server_id in DOC_FIXES:
94+
links = data.get('links', {})
95+
if links.get('documentation') != DOC_FIXES[server_id]:
96+
links['documentation'] = DOC_FIXES[server_id]
97+
data['links'] = links
98+
fixes.append(f"doc URL fixed")
99+
100+
# Fix stytch repo
101+
if server_id == "com.stytch-mcp":
102+
links = data.get('links', {})
103+
links['repository'] = STYTCH_REPO
104+
data['links'] = links
105+
fixes.append("repo URL: org page -> specific repo")
106+
107+
# Fix pagerduty package
108+
if server_id in PACKAGE_FIXES:
109+
transport['args'] = PACKAGE_FIXES[server_id]['args']
110+
fixes.append("fixed package name")
111+
112+
if fixes:
113+
with open(filepath, 'w', encoding='utf-8') as f:
114+
json.dump(data, f, indent=2, ensure_ascii=False)
115+
f.write('\n')
116+
117+
return fixes
118+
119+
def get_remote_branches():
120+
out, _ = run('git for-each-ref --format="%(refname:short)" "refs/remotes/origin/claude/add-*"')
121+
if not out:
122+
return []
123+
return [b.strip().replace('origin/', '') for b in out.strip().split('\n') if b.strip()]
124+
125+
def process_branch(branch_name):
126+
"""Process a single branch: rebase, fix, commit, push."""
127+
short_name = branch_name.replace('claude/add-', '').replace('-mcp-nYwV7', '').replace('-nYwV7', '')
128+
fix_branch = f"fix/{short_name}"
129+
130+
# Create fix branch from the remote branch, rebased on main
131+
run(f'git branch -D {fix_branch}', check=False)
132+
out, err = run(f'git checkout -b {fix_branch} origin/{branch_name}')
133+
if out is None:
134+
return {"branch": branch_name, "status": "ERROR", "error": f"checkout failed: {err}"}
135+
136+
# Rebase onto main
137+
out, err = run('git rebase main')
138+
if out is None:
139+
run('git rebase --abort', check=False)
140+
run('git checkout main', check=False)
141+
run(f'git branch -D {fix_branch}', check=False)
142+
return {"branch": branch_name, "status": "REBASE_CONFLICT", "error": err}
143+
144+
# Find the server definition files that differ from main
145+
out, _ = run('git diff main --name-only -- servers/')
146+
if not out:
147+
run('git checkout main', check=False)
148+
run(f'git branch -D {fix_branch}', check=False)
149+
return {"branch": branch_name, "status": "NO_CHANGES", "error": "No server files differ from main"}
150+
151+
diff_files = [f for f in out.strip().split('\n') if f.strip()]
152+
153+
all_fixes = []
154+
for f in diff_files:
155+
filepath = os.path.join(REPO_DIR, f)
156+
if os.path.exists(filepath) and f.endswith('.json'):
157+
try:
158+
with open(filepath, 'r', encoding='utf-8') as fh:
159+
data = json.load(fh)
160+
server_id = data.get('id', '')
161+
fixes = fix_definition(filepath, server_id)
162+
if fixes:
163+
all_fixes.extend([(f, fix) for fix in fixes])
164+
except Exception as e:
165+
all_fixes.append((f, f"PARSE_ERROR: {e}"))
166+
167+
# Commit fixes if any
168+
if all_fixes:
169+
run('git add -A')
170+
fix_desc = "; ".join([fix for _, fix in all_fixes[:5]])
171+
commit_msg = f"fix: update server definition ({fix_desc})"
172+
run(f'git commit -m "{commit_msg}"')
173+
174+
# Push
175+
out, err = run(f'git push -u origin {fix_branch} --force')
176+
if out is None and "error" in str(err).lower():
177+
result = {"branch": branch_name, "fix_branch": fix_branch, "status": "PUSH_ERROR", "error": err, "fixes": all_fixes}
178+
else:
179+
result = {"branch": branch_name, "fix_branch": fix_branch, "status": "PUSHED", "fixes": all_fixes, "files": diff_files}
180+
181+
# Back to main
182+
run('git checkout main')
183+
184+
return result
185+
186+
def create_pr(fix_branch, server_name, files):
187+
"""Create a PR for a fix branch."""
188+
file_list = ", ".join([os.path.basename(f) for f in files])
189+
title = f"feat: add {server_name} MCP server definition"
190+
body = f"Add {server_name} MCP server definition.\\n\\nFiles: {file_list}"
191+
192+
out, err = run(f'gh pr create --base main --head {fix_branch} --title "{title}" --body "{body}"')
193+
if out and "http" in out:
194+
return out.strip()
195+
return f"PR_ERROR: {err}"
196+
197+
if __name__ == "__main__":
198+
os.chdir(REPO_DIR)
199+
200+
# Ensure we're on main
201+
run('git checkout main')
202+
203+
branches = get_remote_branches()
204+
print(f"Found {len(branches)} branches to process")
205+
206+
results = []
207+
for i, branch in enumerate(branches):
208+
print(f"\n[{i+1}/{len(branches)}] Processing {branch}...")
209+
result = process_branch(branch)
210+
results.append(result)
211+
print(f" Status: {result['status']}")
212+
if result.get('fixes'):
213+
for f, fix in result['fixes']:
214+
print(f" Fix: {fix}")
215+
216+
# Summary
217+
print("\n\n=== SUMMARY ===")
218+
for r in results:
219+
status = r['status']
220+
branch = r['branch']
221+
fixes = len(r.get('fixes', []))
222+
print(f" {status:20s} {branch} ({fixes} fixes)")

fix_ids.py

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
"""
2+
Fix definition IDs:
3+
- Community-driven definitions: use GitHub org as domain (e.g., crystaldba.postgres-mcp-uvx)
4+
- Official vendor definitions: keep current domain (com.redis-mcp, com.mongodb-mcp, etc.)
5+
- Ensure all IDs match filename pattern
6+
7+
Rules:
8+
- If contributor.github matches the repo org AND it's not a major vendor domain (com., io., etc.),
9+
use the GitHub org: githubuser.servername-tool
10+
- For modelcontextprotocol/servers, keep "community." prefix
11+
- For official vendor repos (company.com domains), keep existing convention
12+
"""
13+
import json, subprocess, re, os
14+
15+
os.chdir('d:\\mcpmux\\mcp-servers')
16+
17+
main_files = set(subprocess.check_output(
18+
['git', 'ls-tree', '-r', '--name-only', 'main', '--', 'servers/'],
19+
text=True
20+
).strip().split('\n'))
21+
22+
branches = subprocess.check_output(['git', 'branch', '--list', 'fix/*'], text=True).strip().split('\n')
23+
branches = [b.strip().lstrip('* ') for b in branches if b.strip()]
24+
25+
# Map of current file -> what the ID SHOULD be based on repo org
26+
# community.* prefix servers that should use github org instead
27+
COMMUNITY_REMAP = {
28+
# crystaldba/postgres-mcp -> crystaldba.postgres-mcp-uvx
29+
'community.postgresql-uvx': 'crystaldba.postgres-mcp-uvx',
30+
# domdomegg/airtable-mcp-server -> domdomegg.airtable-mcp-npx
31+
'community.airtable-npx': 'domdomegg.airtable-mcp-npx',
32+
# ThetaBird/mcp-server-axiom-js -> thetabird.axiom-mcp-npx
33+
'community.axiom-npx': 'thetabird.axiom-mcp-npx',
34+
# orellazri/coda-mcp -> orellazri.coda-mcp-npx
35+
'community.coda-npx': 'orellazri.coda-mcp-npx',
36+
# aashari/mcp-server-atlassian-jira -> aashari.jira-mcp-npx
37+
'community.jira-npx': 'aashari.jira-mcp-npx',
38+
# CakeRepository/1Password-MCP -> cakerepository.1password-mcp-npx
39+
'community.1password-npx': 'cakerepository.1password-mcp-npx',
40+
# modelcontextprotocol/servers - these stay community.*
41+
# 'community.fetch-uvx': keep as is
42+
# 'community.memory-npx': keep as is
43+
# 'community.sequential-thinking-npx': keep as is
44+
# 'community.google-maps-npx': keep as is
45+
# 'community.gitlab-npx': keep as is
46+
}
47+
48+
results = []
49+
50+
for branch in sorted(branches):
51+
files_out = subprocess.run(
52+
['git', 'ls-tree', '-r', '--name-only', branch, '--', 'servers/'],
53+
capture_output=True, text=True
54+
)
55+
if files_out.returncode != 0:
56+
continue
57+
58+
all_files = files_out.stdout.strip().split('\n')
59+
new_files = [f for f in all_files if f not in main_files and f.endswith('.json')]
60+
61+
if not new_files:
62+
continue
63+
64+
# Check if any file needs ID remap
65+
needs_fix = False
66+
for f in new_files:
67+
basename = os.path.basename(f).replace('.json', '')
68+
if basename in COMMUNITY_REMAP:
69+
needs_fix = True
70+
break
71+
72+
if not needs_fix:
73+
continue
74+
75+
# Checkout
76+
subprocess.run(['git', 'checkout', branch], capture_output=True, text=True)
77+
78+
changed = False
79+
renames_done = []
80+
81+
for f in new_files:
82+
basename = os.path.basename(f).replace('.json', '')
83+
if basename not in COMMUNITY_REMAP:
84+
continue
85+
86+
new_id = COMMUNITY_REMAP[basename]
87+
new_filename = f"servers/{new_id}.json"
88+
89+
# Read and update
90+
with open(f) as fh:
91+
defn = json.load(fh)
92+
93+
old_id = defn['id']
94+
defn['id'] = new_id
95+
96+
with open(f, 'w') as fh:
97+
json.dump(defn, fh, indent=2)
98+
fh.write('\n')
99+
100+
# Git mv
101+
subprocess.run(['git', 'mv', f, new_filename], capture_output=True, text=True)
102+
renames_done.append((basename, new_id, old_id))
103+
changed = True
104+
105+
if changed:
106+
subprocess.run(['git', 'add', '-A'], capture_output=True, text=True)
107+
msg = "fix: use GitHub org as ID domain for community definitions\n\n"
108+
for old, new, old_id in renames_done:
109+
msg += f"- {old_id} -> {new}\n"
110+
111+
subprocess.run([
112+
'git', 'commit', '-s',
113+
'--author=Mohammod Al Amin Ashik <maa.ashik00@gmail.com>',
114+
'-m', msg
115+
], capture_output=True, text=True)
116+
117+
push = subprocess.run(['git', 'push', 'origin', branch], capture_output=True, text=True)
118+
for old, new, old_id in renames_done:
119+
status = "OK" if push.returncode == 0 else "PUSH_FAIL"
120+
print(f"{status} {branch}: {old_id} -> {new}")
121+
results.append((branch, old_id, new))
122+
123+
subprocess.run(['git', 'checkout', 'main'], capture_output=True, text=True)
124+
print(f"\n=== Fixed {len(results)} community definition IDs ===")

0 commit comments

Comments
 (0)