@pipeworx/mcp-congress 0.1.2 → 0.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/package.json +3 -3
- package/server.json +1 -1
- package/src/committee-index-data.ts +67 -0
- package/src/index.ts +304 -0
- package/src/server.ts +1 -1
package/src/index.ts
CHANGED
|
@@ -672,6 +672,7 @@ function collapse(s: string): string {
|
|
|
672
672
|
}
|
|
673
673
|
/**
|
|
674
674
|
* Congress MCP — US Congress data via GovTrack API (free, no auth required)
|
|
675
|
+
* plus committee rosters/leadership baked from a second, public-domain source.
|
|
675
676
|
*
|
|
676
677
|
* Tools:
|
|
677
678
|
* - search_bills: Search congressional bills by name, subject or keyword, ranked locally by title match
|
|
@@ -679,8 +680,32 @@ function collapse(s: string): string {
|
|
|
679
680
|
* - get_members: Get current members of Congress
|
|
680
681
|
* - get_votes: Get recent congressional votes (newest first)
|
|
681
682
|
* - get_recent_bills: Most recently introduced / acted-on bills
|
|
683
|
+
* - committee_leadership: every standing committee in a chamber with its chair + ranking member
|
|
684
|
+
* - committee_members: one committee's full roster (party, rank, chair/ranking title)
|
|
685
|
+
* - member_committees: one member's committee assignments by bioguide id
|
|
686
|
+
*
|
|
687
|
+
* COMMITTEE DATA (fleet #2843): GovTrack's API has no committee-membership or
|
|
688
|
+
* committee-leadership endpoint at all, so committee_leadership/
|
|
689
|
+
* committee_members/member_committees are baked from a SEPARATE source —
|
|
690
|
+
* github.com/unitedstates/congress-legislators (committees-current.yaml,
|
|
691
|
+
* committee-membership-current.yaml, legislators-current.yaml), CC0 1.0
|
|
692
|
+
* Universal ("no rights reserved" per that repo's own LICENSE, verified
|
|
693
|
+
* live). Refreshed by scripts/refresh-congress-committees.mjs into
|
|
694
|
+
* ./committee-index-data.ts, which is NEVER value-imported here (fleet #2754
|
|
695
|
+
* — a baked table as a static import blew the gateway bundle 24.4->37.8MiB
|
|
696
|
+
* and caused edge 1102s on ~1/4 of requests) — it is registered in
|
|
697
|
+
* workers/gateway/src/pack-baked-indexes.json, uploaded to KV at deploy, and
|
|
698
|
+
* injected as `args._bakedIndex` on every call into this pack. This data
|
|
699
|
+
* changes on a committee-reshuffle cadence, not daily; `data_as_of` on every
|
|
700
|
+
* response is the source files' own last-commit date, not "now".
|
|
682
701
|
*/
|
|
683
702
|
|
|
703
|
+
import type {
|
|
704
|
+
CommitteeRow,
|
|
705
|
+
CommitteeMemberRow,
|
|
706
|
+
MemberCommitteeAssignment,
|
|
707
|
+
MemberProfile,
|
|
708
|
+
} from './committee-index-data.js';
|
|
684
709
|
|
|
685
710
|
// Bound every fetch() in this pack to a fixed timeout — an upstream that
|
|
686
711
|
// degrades without erroring would otherwise hold the Worker in `await fetch()`
|
|
@@ -830,6 +855,243 @@ function formatVote(v: RawVote) {
|
|
|
830
855
|
};
|
|
831
856
|
}
|
|
832
857
|
|
|
858
|
+
// --- Baked committee index (fleet #2843) ---
|
|
859
|
+
//
|
|
860
|
+
// NEVER value-import ./committee-index-data.js here — the type-only import
|
|
861
|
+
// above is erased from the bundle; a value import is not (same trap as
|
|
862
|
+
// mcps/arkansas-code/src/index.ts's AR_SECTIONS, fleet #2754).
|
|
863
|
+
let COMMITTEES: CommitteeRow[] = [];
|
|
864
|
+
let COMMITTEE_MEMBERSHIP: Record<string, CommitteeMemberRow[]> = {};
|
|
865
|
+
let MEMBER_COMMITTEES: Record<string, MemberCommitteeAssignment[]> = {};
|
|
866
|
+
let MEMBER_PROFILES: Record<string, MemberProfile> = {};
|
|
867
|
+
let CONGRESS_DATA_AS_OF = '';
|
|
868
|
+
let COMMITTEE_INDEX_LOADED = false;
|
|
869
|
+
|
|
870
|
+
function ensureCommitteeIndex(args: Record<string, unknown>): void {
|
|
871
|
+
if (COMMITTEE_INDEX_LOADED) return;
|
|
872
|
+
const idx = args._bakedIndex as Record<string, unknown> | null | undefined;
|
|
873
|
+
const committees = idx?.COMMITTEES;
|
|
874
|
+
const membership = idx?.COMMITTEE_MEMBERSHIP;
|
|
875
|
+
const memberCommittees = idx?.MEMBER_COMMITTEES;
|
|
876
|
+
const memberProfiles = idx?.MEMBER_PROFILES;
|
|
877
|
+
const dataAsOf = idx?.CONGRESS_DATA_AS_OF;
|
|
878
|
+
const ok =
|
|
879
|
+
Array.isArray(committees) &&
|
|
880
|
+
committees.length > 0 &&
|
|
881
|
+
typeof membership === 'object' &&
|
|
882
|
+
membership !== null &&
|
|
883
|
+
typeof memberCommittees === 'object' &&
|
|
884
|
+
memberCommittees !== null &&
|
|
885
|
+
typeof memberProfiles === 'object' &&
|
|
886
|
+
memberProfiles !== null &&
|
|
887
|
+
typeof dataAsOf === 'string' &&
|
|
888
|
+
dataAsOf.length > 0;
|
|
889
|
+
if (!ok) {
|
|
890
|
+
// Loud on purpose, same posture as ar-code's ensureIndex: a missing or
|
|
891
|
+
// malformed baked index is a server-side KV fault, not "no committees
|
|
892
|
+
// exist" — collapsing the two would be exactly the silent-empty-array
|
|
893
|
+
// trap docs/silent-zero-policy.md holds every pack to.
|
|
894
|
+
throw new Error(
|
|
895
|
+
`congress: the baked committee index is unavailable (${idx == null ? 'not injected' : 'malformed'}). ` +
|
|
896
|
+
'The gateway loads it from KV on the first call into committee_leadership/committee_members/member_committees; ' +
|
|
897
|
+
'this is a server-side fault, not an empty roster — retry shortly. search_bills/get_bill/get_members/get_votes/get_recent_bills are unaffected (they call GovTrack live and never touch this index).',
|
|
898
|
+
);
|
|
899
|
+
}
|
|
900
|
+
COMMITTEES = committees as CommitteeRow[];
|
|
901
|
+
COMMITTEE_MEMBERSHIP = membership as Record<string, CommitteeMemberRow[]>;
|
|
902
|
+
MEMBER_COMMITTEES = memberCommittees as Record<string, MemberCommitteeAssignment[]>;
|
|
903
|
+
MEMBER_PROFILES = memberProfiles as Record<string, MemberProfile>;
|
|
904
|
+
CONGRESS_DATA_AS_OF = dataAsOf;
|
|
905
|
+
COMMITTEE_INDEX_LOADED = true;
|
|
906
|
+
}
|
|
907
|
+
|
|
908
|
+
const VALID_CHAMBERS = new Set(['house', 'senate', 'joint']);
|
|
909
|
+
|
|
910
|
+
/**
|
|
911
|
+
* Chair/ranking member resolution: prefer the explicit title (handles
|
|
912
|
+
* "Chair"/"Chairman"/"Chairwoman" and "Ranking Member"); fall back to
|
|
913
|
+
* majority/minority rank 1 for the handful of caucuses and select
|
|
914
|
+
* committees (e.g. the Senate Intelligence/Ethics committees, the Joint
|
|
915
|
+
* Commission on Security and Cooperation in Europe) that the source data
|
|
916
|
+
* does not label with an explicit title.
|
|
917
|
+
*/
|
|
918
|
+
function resolveLeadership(members: CommitteeMemberRow[]): {
|
|
919
|
+
chair: CommitteeMemberRow | null;
|
|
920
|
+
ranking_member: CommitteeMemberRow | null;
|
|
921
|
+
} {
|
|
922
|
+
const chair =
|
|
923
|
+
members.find((m) => typeof m.title === 'string' && /^Chair/i.test(m.title)) ??
|
|
924
|
+
members.find((m) => m.party_role === 'majority' && m.rank === 1) ??
|
|
925
|
+
null;
|
|
926
|
+
const ranking =
|
|
927
|
+
members.find((m) => m.title === 'Ranking Member') ??
|
|
928
|
+
members.find((m) => m.party_role === 'minority' && m.rank === 1) ??
|
|
929
|
+
null;
|
|
930
|
+
return { chair, ranking_member: ranking };
|
|
931
|
+
}
|
|
932
|
+
|
|
933
|
+
/** Words that appear in nearly every committee name and carry no identifying signal. */
|
|
934
|
+
const COMMITTEE_NAME_STOPWORDS = new Set([
|
|
935
|
+
'house', 'senate', 'joint', 'committee', 'committees', 'on', 'the', 'select',
|
|
936
|
+
'permanent', 'special', 'of', 'congress', 'united', 'states', 'us', 'subcommittee',
|
|
937
|
+
'and', 'for',
|
|
938
|
+
]);
|
|
939
|
+
|
|
940
|
+
function normalizeCommitteeName(s: string): string[] {
|
|
941
|
+
return s
|
|
942
|
+
.toLowerCase()
|
|
943
|
+
.replace(/[^a-z0-9]+/g, ' ')
|
|
944
|
+
.trim()
|
|
945
|
+
.split(' ')
|
|
946
|
+
.filter((w) => w && !COMMITTEE_NAME_STOPWORDS.has(w));
|
|
947
|
+
}
|
|
948
|
+
|
|
949
|
+
/** Full-committee (not subcommittee) name search — every query word must appear in (or be a prefix/suffix of) a candidate word. */
|
|
950
|
+
function findCommitteesByName(name: string, chamber: string | undefined): CommitteeRow[] {
|
|
951
|
+
const qWords = normalizeCommitteeName(name);
|
|
952
|
+
if (qWords.length === 0) return [];
|
|
953
|
+
return COMMITTEES.filter((c) => {
|
|
954
|
+
if (chamber && c.chamber !== chamber) return false;
|
|
955
|
+
const cWords = normalizeCommitteeName(c.name);
|
|
956
|
+
return qWords.every((w) => cWords.some((cw) => cw === w || cw.startsWith(w) || w.startsWith(cw)));
|
|
957
|
+
});
|
|
958
|
+
}
|
|
959
|
+
|
|
960
|
+
function findCommitteeById(id: string): CommitteeRow | undefined {
|
|
961
|
+
const norm = id.trim().toUpperCase();
|
|
962
|
+
return COMMITTEES.find((c) => c.id.toUpperCase() === norm);
|
|
963
|
+
}
|
|
964
|
+
|
|
965
|
+
function buildFullCommittee(c: CommitteeRow) {
|
|
966
|
+
const members = COMMITTEE_MEMBERSHIP[c.id] ?? [];
|
|
967
|
+
const { chair, ranking_member } = resolveLeadership(members);
|
|
968
|
+
return {
|
|
969
|
+
id: c.id,
|
|
970
|
+
name: c.name,
|
|
971
|
+
chamber: c.chamber,
|
|
972
|
+
url: c.url,
|
|
973
|
+
minority_url: c.minority_url,
|
|
974
|
+
jurisdiction: c.jurisdiction,
|
|
975
|
+
chair,
|
|
976
|
+
ranking_member,
|
|
977
|
+
member_count: members.length,
|
|
978
|
+
members,
|
|
979
|
+
subcommittees: c.subcommittees,
|
|
980
|
+
};
|
|
981
|
+
}
|
|
982
|
+
|
|
983
|
+
function committeeLeadership(args: Record<string, unknown>) {
|
|
984
|
+
ensureCommitteeIndex(args);
|
|
985
|
+
const chamberArg = typeof args.chamber === 'string' ? args.chamber.trim().toLowerCase() : undefined;
|
|
986
|
+
if (chamberArg && !VALID_CHAMBERS.has(chamberArg)) {
|
|
987
|
+
return { error: `chamber must be "house", "senate" or "joint" — got ${JSON.stringify(args.chamber)}`, chamber: args.chamber ?? null };
|
|
988
|
+
}
|
|
989
|
+
const committees = COMMITTEES.filter((c) => !chamberArg || c.chamber === chamberArg);
|
|
990
|
+
return {
|
|
991
|
+
chamber: chamberArg ?? 'all',
|
|
992
|
+
congress: 119,
|
|
993
|
+
data_as_of: CONGRESS_DATA_AS_OF,
|
|
994
|
+
source: 'github.com/unitedstates/congress-legislators (public domain, CC0 1.0 Universal)',
|
|
995
|
+
total: committees.length,
|
|
996
|
+
committees: committees.map((c) => {
|
|
997
|
+
const members = COMMITTEE_MEMBERSHIP[c.id] ?? [];
|
|
998
|
+
const { chair, ranking_member } = resolveLeadership(members);
|
|
999
|
+
return {
|
|
1000
|
+
id: c.id,
|
|
1001
|
+
name: c.name,
|
|
1002
|
+
chamber: c.chamber,
|
|
1003
|
+
url: c.url,
|
|
1004
|
+
chair,
|
|
1005
|
+
ranking_member,
|
|
1006
|
+
member_count: members.length,
|
|
1007
|
+
};
|
|
1008
|
+
}),
|
|
1009
|
+
};
|
|
1010
|
+
}
|
|
1011
|
+
|
|
1012
|
+
function committeeMembers(args: Record<string, unknown>) {
|
|
1013
|
+
ensureCommitteeIndex(args);
|
|
1014
|
+
const chamberArg = typeof args.chamber === 'string' ? args.chamber.trim().toLowerCase() : undefined;
|
|
1015
|
+
if (chamberArg && !VALID_CHAMBERS.has(chamberArg)) {
|
|
1016
|
+
return { error: `chamber must be "house", "senate" or "joint" — got ${JSON.stringify(args.chamber)}`, chamber: args.chamber ?? null };
|
|
1017
|
+
}
|
|
1018
|
+
const idArg = typeof args.committee_id === 'string' && args.committee_id.trim() ? args.committee_id.trim() : undefined;
|
|
1019
|
+
const nameArg = typeof args.name === 'string' && args.name.trim() ? args.name.trim() : undefined;
|
|
1020
|
+
if (!idArg && !nameArg) {
|
|
1021
|
+
return { error: 'Pass committee_id (e.g. "HSWM") or name (e.g. "Ways and Means", "Appropriations") — call committee_leadership first to see every valid id/name.' };
|
|
1022
|
+
}
|
|
1023
|
+
|
|
1024
|
+
if (idArg) {
|
|
1025
|
+
const top = findCommitteeById(idArg);
|
|
1026
|
+
if (top) return { data_as_of: CONGRESS_DATA_AS_OF, ...buildFullCommittee(top) };
|
|
1027
|
+
for (const c of COMMITTEES) {
|
|
1028
|
+
const sub = c.subcommittees.find((s) => s.id.toUpperCase() === idArg.toUpperCase());
|
|
1029
|
+
if (sub) {
|
|
1030
|
+
const members = COMMITTEE_MEMBERSHIP[sub.id] ?? [];
|
|
1031
|
+
const { chair, ranking_member } = resolveLeadership(members);
|
|
1032
|
+
return {
|
|
1033
|
+
data_as_of: CONGRESS_DATA_AS_OF,
|
|
1034
|
+
id: sub.id,
|
|
1035
|
+
name: `${c.name} Subcommittee on ${sub.name}`,
|
|
1036
|
+
chamber: c.chamber,
|
|
1037
|
+
parent_committee_id: c.id,
|
|
1038
|
+
parent_committee_name: c.name,
|
|
1039
|
+
chair,
|
|
1040
|
+
ranking_member,
|
|
1041
|
+
member_count: members.length,
|
|
1042
|
+
members,
|
|
1043
|
+
};
|
|
1044
|
+
}
|
|
1045
|
+
}
|
|
1046
|
+
return {
|
|
1047
|
+
found: false,
|
|
1048
|
+
error: `No committee or subcommittee with id "${idArg}". Call committee_leadership to list valid ids.`,
|
|
1049
|
+
committee_id: idArg,
|
|
1050
|
+
};
|
|
1051
|
+
}
|
|
1052
|
+
|
|
1053
|
+
const matches = findCommitteesByName(nameArg!, chamberArg);
|
|
1054
|
+
if (matches.length === 0) {
|
|
1055
|
+
return {
|
|
1056
|
+
found: false,
|
|
1057
|
+
error: `No committee name matched "${nameArg}"${chamberArg ? ` in the ${chamberArg}` : ''}. Try a shorter name (e.g. "Appropriations", "Judiciary", "Ways and Means"), or pass chamber to disambiguate.`,
|
|
1058
|
+
name: nameArg,
|
|
1059
|
+
};
|
|
1060
|
+
}
|
|
1061
|
+
if (matches.length === 1) {
|
|
1062
|
+
return { data_as_of: CONGRESS_DATA_AS_OF, ...buildFullCommittee(matches[0]) };
|
|
1063
|
+
}
|
|
1064
|
+
return {
|
|
1065
|
+
data_as_of: CONGRESS_DATA_AS_OF,
|
|
1066
|
+
ambiguous: true,
|
|
1067
|
+
note: `"${nameArg}" matched ${matches.length} committees (one per chamber, usually) — pass chamber: "house" or "senate" to pick one.`,
|
|
1068
|
+
matches: matches.map(buildFullCommittee),
|
|
1069
|
+
};
|
|
1070
|
+
}
|
|
1071
|
+
|
|
1072
|
+
function memberCommittees(args: Record<string, unknown>) {
|
|
1073
|
+
ensureCommitteeIndex(args);
|
|
1074
|
+
const bioguide = typeof args.bioguide_id === 'string' ? args.bioguide_id.trim().toUpperCase() : undefined;
|
|
1075
|
+
if (!bioguide) {
|
|
1076
|
+
return { error: 'Pass bioguide_id — a Congress.gov/GovInfo "bioguide" id, e.g. "N000015" for Richard Neal, "G000386" for Chuck Grassley.' };
|
|
1077
|
+
}
|
|
1078
|
+
const assignments = MEMBER_COMMITTEES[bioguide];
|
|
1079
|
+
if (!assignments || assignments.length === 0) {
|
|
1080
|
+
return {
|
|
1081
|
+
found: false,
|
|
1082
|
+
error: `No committee assignments found for bioguide "${bioguide}". This covers the current (119th) Congress only; check the id is correct and the member currently serves.`,
|
|
1083
|
+
bioguide_id: bioguide,
|
|
1084
|
+
};
|
|
1085
|
+
}
|
|
1086
|
+
return {
|
|
1087
|
+
data_as_of: CONGRESS_DATA_AS_OF,
|
|
1088
|
+
bioguide_id: bioguide,
|
|
1089
|
+
profile: MEMBER_PROFILES[bioguide] ?? null,
|
|
1090
|
+
total: assignments.length,
|
|
1091
|
+
committees: assignments,
|
|
1092
|
+
};
|
|
1093
|
+
}
|
|
1094
|
+
|
|
833
1095
|
// --- Tool definitions ---
|
|
834
1096
|
|
|
835
1097
|
const tools: McpToolExport['tools'] = [
|
|
@@ -916,6 +1178,42 @@ const tools: McpToolExport['tools'] = [
|
|
|
916
1178
|
},
|
|
917
1179
|
},
|
|
918
1180
|
},
|
|
1181
|
+
{
|
|
1182
|
+
name: 'committee_leadership',
|
|
1183
|
+
description:
|
|
1184
|
+
'ANSWERS "who chairs X committee" / "who are the ranking members of every House committee" / "list of committee chairs in Congress" — returns EVERY standing committee in one chamber (or all three chambers) of the 119th Congress with its chair and ranking member (name, party, bioguide id). Pass chamber to scope to "house", "senate" or "joint"; omit it for all three. For one committee\'s FULL roster (every member, not just the chair/ranking pair) use committee_members instead. SOURCE: github.com/unitedstates/congress-legislators (public domain), baked periodically — NOT GovTrack, which has no committee-leadership endpoint at all. data_as_of in the response is the source data\'s own last-update date.',
|
|
1185
|
+
inputSchema: {
|
|
1186
|
+
type: 'object',
|
|
1187
|
+
properties: {
|
|
1188
|
+
chamber: { type: 'string', description: '"house", "senate" or "joint". Omit to return every chamber.' },
|
|
1189
|
+
},
|
|
1190
|
+
},
|
|
1191
|
+
},
|
|
1192
|
+
{
|
|
1193
|
+
name: 'committee_members',
|
|
1194
|
+
description:
|
|
1195
|
+
'ANSWERS "who is on the House Appropriations committee" / "roster of the Senate Judiciary committee" / "Democratic members of Ways and Means" — returns one committee\'s full membership with each member\'s party, rank and title (Chair / Ranking Member / plain member), plus the committee\'s chair, ranking member and list of subcommittees. Identify the committee by committee_id (the exact id from committee_leadership, e.g. "HSWM" for House Ways and Means, or a subcommittee id like "HSWM05") OR by name (e.g. "Appropriations", "Ways and Means", "Judiciary") — a name with no chamber that matches committees in BOTH the House and Senate (e.g. "Appropriations") returns both as `matches` rather than guessing; pass chamber to pick one directly. For the chair/ranking member of EVERY committee at once, use committee_leadership instead — it is cheaper when you do not need the full roster. SOURCE: github.com/unitedstates/congress-legislators (public domain), baked periodically, current (119th) Congress only.',
|
|
1196
|
+
inputSchema: {
|
|
1197
|
+
type: 'object',
|
|
1198
|
+
properties: {
|
|
1199
|
+
committee_id: { type: 'string', description: 'Exact committee or subcommittee id, e.g. "HSWM" (House Ways and Means) or "SSJU" (Senate Judiciary). Takes priority over name.' },
|
|
1200
|
+
name: { type: 'string', description: 'Committee name or partial name to search for, e.g. "Appropriations", "Judiciary", "Foreign Affairs".' },
|
|
1201
|
+
chamber: { type: 'string', description: 'Optional: "house", "senate" or "joint" — disambiguates a name that matches more than one chamber.' },
|
|
1202
|
+
},
|
|
1203
|
+
},
|
|
1204
|
+
},
|
|
1205
|
+
{
|
|
1206
|
+
name: 'member_committees',
|
|
1207
|
+
description:
|
|
1208
|
+
'ANSWERS "what committees is X on" / "committee assignments for Senator X" — returns every committee and subcommittee assignment (committee name, chamber, rank, title) for one member of the 119th Congress, identified by their bioguide id (the standard Congress.gov/GovInfo member identifier, e.g. "N000015" for Richard Neal — committee_leadership and committee_members both return bioguide ids for the members they list, so look a member up there first if you only have a name). SOURCE: github.com/unitedstates/congress-legislators (public domain), baked periodically, current Congress only.',
|
|
1209
|
+
inputSchema: {
|
|
1210
|
+
type: 'object',
|
|
1211
|
+
properties: {
|
|
1212
|
+
bioguide_id: { type: 'string', description: 'The member\'s bioguide id, e.g. "N000015".' },
|
|
1213
|
+
},
|
|
1214
|
+
required: ['bioguide_id'],
|
|
1215
|
+
},
|
|
1216
|
+
},
|
|
919
1217
|
];
|
|
920
1218
|
|
|
921
1219
|
// --- callTool dispatcher ---
|
|
@@ -936,6 +1234,12 @@ async function callTool(name: string, args: Record<string, unknown>): Promise<un
|
|
|
936
1234
|
args.congress as number | undefined,
|
|
937
1235
|
(args.limit as number) ?? 20,
|
|
938
1236
|
);
|
|
1237
|
+
case 'committee_leadership':
|
|
1238
|
+
return committeeLeadership(args);
|
|
1239
|
+
case 'committee_members':
|
|
1240
|
+
return committeeMembers(args);
|
|
1241
|
+
case 'member_committees':
|
|
1242
|
+
return memberCommittees(args);
|
|
939
1243
|
default:
|
|
940
1244
|
throw new Error(`Unknown tool: ${name}`);
|
|
941
1245
|
}
|
package/src/server.ts
CHANGED
|
@@ -9,7 +9,7 @@ import { CallToolRequestSchema, ListToolsRequestSchema } from '@modelcontextprot
|
|
|
9
9
|
import pack from './index.js';
|
|
10
10
|
|
|
11
11
|
const server = new Server(
|
|
12
|
-
{ name: '@pipeworx/mcp-congress', version: '0.1.
|
|
12
|
+
{ name: '@pipeworx/mcp-congress', version: '0.1.3' },
|
|
13
13
|
{ capabilities: { tools: {} } },
|
|
14
14
|
);
|
|
15
15
|
|